You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8026 lines
272 KiB
8026 lines
272 KiB
/*++
|
|
|
|
Copyright (c) 1990-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
IPROUTE.C
|
|
|
|
Abstract:
|
|
|
|
This file contains all the route table manipulation code
|
|
|
|
Author:
|
|
|
|
|
|
[Environment:]
|
|
|
|
kernel mode only
|
|
|
|
[Notes:]
|
|
|
|
optional-notes
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
//*** iproute.c - IP routing routines.
|
|
//
|
|
// This file contains all the routines related to IP routing, including
|
|
// routing table lookup and management routines.
|
|
|
|
#include "precomp.h"
|
|
#include "info.h"
|
|
#include "iproute.h"
|
|
#include "iprtdef.h"
|
|
#include "lookup.h"
|
|
#include "ipxmit.h"
|
|
#include "igmp.h"
|
|
#include "mdlpool.h"
|
|
#include "pplasl.h"
|
|
#include "tcpipbuf.h"
|
|
|
|
extern uint LoopIndex;
|
|
extern uint IPSecStatus;
|
|
|
|
typedef struct ChangeNotifyEvent {
|
|
CTEEvent cne_event;
|
|
IPNotifyOutput cne_info;
|
|
LIST_ENTRY *cne_queue;
|
|
void *cne_lock;
|
|
} ChangeNotifyEvent;
|
|
|
|
void ChangeNotifyAsync(CTEEvent *Event, PVOID Context);
|
|
|
|
void InvalidateRCEChain(RouteTableEntry * RTE);
|
|
|
|
extern IPAddr g_ValidAddr;
|
|
|
|
extern uint TotalFreeInterfaces;
|
|
extern uint MaxFreeInterfaces;
|
|
extern Interface *FrontFreeList;
|
|
extern Interface *RearFreeList;
|
|
|
|
|
|
|
|
RouteCacheEntry *RCEFreeList = NULL;
|
|
|
|
extern void DampCheck(void);
|
|
|
|
#if IPMCAST
|
|
|
|
#define MCAST_STARTED 1
|
|
extern uint g_dwMcastState;
|
|
|
|
extern BOOLEAN IPMForwardAfterRcv(NetTableEntry *PrimarySrcNTE,
|
|
IPHeader UNALIGNED *Header, uint HeaderLength,
|
|
PVOID Data, uint BufferLength,
|
|
NDIS_HANDLE LContext1, uint LContext2,
|
|
uchar DestType, LinkEntry *LinkCtxt);
|
|
|
|
extern BOOLEAN IPMForwardAfterRcvPkt(NetTableEntry *PrimarySrcNTE,
|
|
IPHeader UNALIGNED *Header,
|
|
uint HeaderLength,
|
|
PVOID Data, uint BufferLength,
|
|
NDIS_HANDLE LContext1, uint LContext2,
|
|
uchar DestType, uint MacHeaderSize,
|
|
PNDIS_BUFFER NdisBuffer,
|
|
uint* pClientCnt, LinkEntry * LinkCtxt);
|
|
#endif
|
|
|
|
ulong DbgNumPktFwd = 0;
|
|
|
|
ulong UnConnected = 0;
|
|
RouteCacheEntry *UnConnectedRCE;
|
|
ulong Rcefailures = 0;
|
|
|
|
extern NetTableEntry **NewNetTableList; // hash table for NTEs
|
|
extern uint NET_TABLE_SIZE;
|
|
extern RefPtr DHCPRefPtr; // Referenced pointer to NTE being DHCP'd.
|
|
|
|
extern NetTableEntry *LoopNTE; // Pointer to loopback NTE.
|
|
extern Interface LoopInterface; // Pointer to loopback interface.
|
|
|
|
extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong, uchar);
|
|
extern IP_STATUS SendICMPIPSecErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong);
|
|
extern uchar ParseRcvdOptions(IPOptInfo *, OptIndex *);
|
|
extern void ULMTUNotify(IPAddr Dest, IPAddr Src, uchar Prot, void *Ptr,
|
|
uint NewMTU);
|
|
void EnableRouter();
|
|
void DisableRouter();
|
|
|
|
IPHeader *GetFWPacket(PNDIS_PACKET *ReturnedPacket);
|
|
void FreeFWPacket(PNDIS_PACKET Packet);
|
|
PNDIS_BUFFER GetFWBufferChain(uint DataLength, PNDIS_PACKET Packet,
|
|
PNDIS_BUFFER *TailPointer);
|
|
BOOLEAN InitForwardingPools();
|
|
|
|
PVOID
|
|
NTAPI
|
|
FwPacketAllocate (
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag
|
|
);
|
|
|
|
VOID
|
|
NTAPI
|
|
FwPacketFree (
|
|
IN PVOID Buffer
|
|
);
|
|
|
|
extern Interface *IFList;
|
|
extern NDIS_HANDLE BufferPool;
|
|
|
|
extern CTEBlockStruc TcpipUnloadBlock; // Structure for blocking at time of unload
|
|
extern BOOLEAN fRouteTimerStopping;
|
|
void IPDelNTE(NetTableEntry * NTE, CTELockHandle * RouteTableHandle);
|
|
|
|
CACHE_LINE_KSPIN_LOCK RouteTableLock;
|
|
LIST_ENTRY RtChangeNotifyQueue;
|
|
LIST_ENTRY RtChangeNotifyQueueEx;
|
|
|
|
extern HANDLE IpHeaderPool;
|
|
|
|
NDIS_HANDLE IpForwardPacketPool;
|
|
HANDLE IpForwardLargePool;
|
|
HANDLE IpForwardSmallPool;
|
|
|
|
// Buffer size calculation: Based on the MDL pool's implementation:
|
|
// sizeof(POOL_HEADER) + N * ALIGN_UP(sizeof(MDL) + BufSize, PVOID) == PAGE_SIZE
|
|
// N is the number of buffers per page.
|
|
// Choose BufSize to minimize wasted space per page
|
|
//
|
|
#ifdef _WIN64
|
|
// Chosen to get 5 buffers per pool page with minimal space wasted.
|
|
#define BUFSIZE_LARGE_POOL 1576
|
|
// Chosen to get 9 buffers per pool page with no space wasted.
|
|
#define BUFSIZE_SMALL_POOL 856
|
|
#else
|
|
// Chosen to get 3 buffers per pool page with 8 bytes wasted.
|
|
#define BUFSIZE_LARGE_POOL 1320
|
|
// Chosen to get 8 buffers per pool page with no space wasted.
|
|
#define BUFSIZE_SMALL_POOL 476
|
|
#endif
|
|
|
|
#define PACKET_POOL_SIZE 16*1024
|
|
|
|
|
|
uchar ForwardBCast; // Flag indicating if we should forward bcasts.
|
|
uchar ForwardPackets; // Flag indicating whether we should forward.
|
|
uchar RouterConfigured; // TRUE if we were initially configured as a
|
|
// router.
|
|
int IPEnableRouterRefCount; // Tracks enables/disables of
|
|
// routing by various services
|
|
RouteSendQ *BCastRSQ;
|
|
|
|
uint DefGWConfigured; // Number of default gateways configed.
|
|
uint DefGWActive; // Number of def. gateways active.
|
|
uint DeadGWDetect;
|
|
uint PMTUDiscovery;
|
|
|
|
ProtInfo *RtPI = NULL;
|
|
|
|
IPMask IPMaskTable[] =
|
|
{
|
|
CLASSA_MASK,
|
|
CLASSA_MASK,
|
|
CLASSA_MASK,
|
|
CLASSA_MASK,
|
|
CLASSA_MASK,
|
|
CLASSA_MASK,
|
|
CLASSA_MASK,
|
|
CLASSA_MASK,
|
|
CLASSB_MASK,
|
|
CLASSB_MASK,
|
|
CLASSB_MASK,
|
|
CLASSB_MASK,
|
|
CLASSC_MASK,
|
|
CLASSC_MASK,
|
|
CLASSD_MASK,
|
|
CLASSE_MASK};
|
|
|
|
extern void TransmitFWPacket(PNDIS_PACKET, uint);
|
|
|
|
uint MTUTable[] =
|
|
{
|
|
65535 - sizeof(IPHeader),
|
|
32000 - sizeof(IPHeader),
|
|
17914 - sizeof(IPHeader),
|
|
8166 - sizeof(IPHeader),
|
|
4352 - sizeof(IPHeader),
|
|
2002 - sizeof(IPHeader),
|
|
1492 - sizeof(IPHeader),
|
|
1006 - sizeof(IPHeader),
|
|
508 - sizeof(IPHeader),
|
|
296 - sizeof(IPHeader),
|
|
MIN_VALID_MTU - sizeof(IPHeader)
|
|
};
|
|
|
|
uint DisableIPSourceRouting = 1;
|
|
|
|
CTETimer IPRouteTimer;
|
|
|
|
// Referenced pointer to callout routine for dial on demand.
|
|
RefPtr DODRefPtr;
|
|
|
|
// Referenced pointer to packet filter callout routine.
|
|
RefPtr FilterRefPtr;
|
|
|
|
RouteInterface DummyInterface; // Dummy interface.
|
|
|
|
#if FFP_SUPPORT
|
|
ULONG FFPRegFastForwardingCacheSize; // FFP Configuration Params
|
|
ULONG FFPRegControlFlags; // from the System Registry
|
|
|
|
ULONG FFPFlushRequired; // Whether an FFP Cache Flush is needed
|
|
#endif // if FFP_SUPPORT
|
|
|
|
ULONG RouteTimerTicks; // To simulate 2 timers with different granularity
|
|
|
|
ULONG FlushIFTimerTicks; // To simulate 2 timers with different granularity
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
//
|
|
// Make init code disposable.
|
|
//
|
|
int InitRouting(IPConfigInfo * ci);
|
|
|
|
#pragma alloc_text(INIT, InitRouting)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
// this macro is called whenever we delete the route: takes care of routes on links
|
|
#define CleanupP2MP_RTE(_RTE) { \
|
|
if ((_RTE)->rte_link){ \
|
|
LinkEntry *Link; \
|
|
RouteTableEntry *PrvRte, *tmpRte; \
|
|
Link = (_RTE)->rte_link; \
|
|
PrvRte = Link->link_rte; \
|
|
tmpRte = Link->link_rte; \
|
|
while (tmpRte){ \
|
|
if (tmpRte == (_RTE)) break; \
|
|
PrvRte = tmpRte; \
|
|
tmpRte = tmpRte->rte_nextlinkrte; \
|
|
} \
|
|
if (tmpRte) { \
|
|
if (PrvRte == tmpRte) { \
|
|
Link->link_rte = (_RTE)->rte_nextlinkrte; \
|
|
} else { \
|
|
PrvRte->rte_nextlinkrte = (_RTE)->rte_nextlinkrte; \
|
|
} \
|
|
} else { \
|
|
ASSERT((FALSE)); \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
|
|
//** GetIfConstraint - Decide whether to constrain a lookup
|
|
//
|
|
// Arguments: Dest - destination address
|
|
// Src - source address
|
|
// OptInfo - options to use for a lookup
|
|
// fIpsec - IPsec reinjected packet
|
|
//
|
|
// Returns: IfIndex to constrain lookup to,
|
|
// 0 if unconstrained
|
|
// INVALID_IF_INDEX if constrained by source address only
|
|
//
|
|
uint
|
|
GetIfConstraint(IPAddr Dest, IPAddr Src, IPOptInfo *OptInfo, BOOLEAN fIpsec)
|
|
{
|
|
uint ConstrainIF=0;
|
|
|
|
if (CLASSD_ADDR(Dest)) {
|
|
ConstrainIF = (OptInfo)? OptInfo->ioi_mcastif : 0;
|
|
if (!ConstrainIF && Src && !fIpsec) {
|
|
ConstrainIF = INVALID_IF_INDEX;
|
|
}
|
|
} else {
|
|
ConstrainIF = (OptInfo)? OptInfo->ioi_ucastif : 0;
|
|
}
|
|
|
|
return ConstrainIF;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
InvalidateRCEContext(RouteCacheEntry *RCE)
|
|
{
|
|
Interface *IF, *tmpIF = NULL;
|
|
|
|
ASSERT(RCE->rce_flags & RCE_CONNECTED);
|
|
|
|
IF = (Interface *) RCE->rce_rte;
|
|
|
|
if (RCE->rce_flags & RCE_REFERENCED) {
|
|
|
|
//
|
|
// If we hold a reference on the interface,
|
|
// it is guaranteed the interface won't go away.
|
|
//
|
|
|
|
(*(IF->if_invalidate)) (IF->if_lcontext, RCE);
|
|
LockedDerefIF(IF);
|
|
RCE->rce_flags &= ~RCE_REFERENCED;
|
|
} else {
|
|
|
|
//
|
|
// In the case we do not hold a reference on the interface,
|
|
// we need to make sure the IF is still there.
|
|
//
|
|
|
|
for (tmpIF = IFList; tmpIF != NULL; tmpIF = tmpIF->if_next) {
|
|
if (tmpIF == IF) break;
|
|
}
|
|
if (tmpIF) {
|
|
(*(IF->if_invalidate)) (IF->if_lcontext, RCE);
|
|
} else {
|
|
RtlZeroMemory(RCE->rce_context, RCE_CONTEXT_SIZE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//** DummyFilterPtr - Dummy filter-driver callout-routine
|
|
//
|
|
// A dummy routine installed while a real callout is in the process of being
|
|
// deregistered.
|
|
//
|
|
// Entry: no arguments used.
|
|
//
|
|
// Returns: FORWARD.
|
|
//
|
|
FORWARD_ACTION
|
|
DummyFilterPtr(struct IPHeader UNALIGNED* PacketHeader,
|
|
uchar* Packet, uint PacketLength,
|
|
uint RecvInterfaceIndex, uint SendInterfaceIndex,
|
|
IPAddr RecvLinkNextHop, IPAddr SendLinkNextHop)
|
|
{
|
|
UNREFERENCED_PARAMETER(PacketHeader);
|
|
UNREFERENCED_PARAMETER(Packet);
|
|
UNREFERENCED_PARAMETER(PacketLength);
|
|
UNREFERENCED_PARAMETER(RecvInterfaceIndex);
|
|
UNREFERENCED_PARAMETER(SendInterfaceIndex);
|
|
UNREFERENCED_PARAMETER(RecvLinkNextHop);
|
|
UNREFERENCED_PARAMETER(SendLinkNextHop);
|
|
|
|
return FORWARD;
|
|
}
|
|
|
|
//** DummyDODCallout - Dummy dial-on-demand callout-routine
|
|
//
|
|
// A dummy routine installed while a real callout is in the process of being
|
|
// deregistered.
|
|
//
|
|
// Entry: no arguments used.
|
|
//
|
|
// Returns: INVALID_IF_INDEX.
|
|
//
|
|
uint
|
|
DummyDODCallout(ROUTE_CONTEXT Context, IPAddr Destination, IPAddr Source,
|
|
uchar Protocol, uchar *Buffer, uint Length, IPAddr HdrSrc)
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(Destination);
|
|
UNREFERENCED_PARAMETER(Source);
|
|
UNREFERENCED_PARAMETER(Protocol);
|
|
UNREFERENCED_PARAMETER(Buffer);
|
|
UNREFERENCED_PARAMETER(Length);
|
|
UNREFERENCED_PARAMETER(HdrSrc);
|
|
|
|
return INVALID_IF_INDEX;
|
|
}
|
|
|
|
|
|
//** NotifyFilterOfDiscard - notify the filter before discarding a packet
|
|
//
|
|
// Called when a packet is to be dropped before the filtering step is done.
|
|
// This allows the dropped packet to be logged, if necessary.
|
|
//
|
|
// Entry: NTE - receiving NTE
|
|
// IPH - header of dropped packet
|
|
// Data - payload of dropped packet
|
|
// DataSize - length of bytes at 'Data'.
|
|
//
|
|
// Returns: TRUE if IP filter-driver returned 'FORWARD', FALSE otherwise.
|
|
//
|
|
BOOLEAN
|
|
NotifyFilterOfDiscard(NetTableEntry* NTE, IPHeader UNALIGNED* IPH, uchar* Data,
|
|
uint DataSize)
|
|
{
|
|
FORWARD_ACTION Action;
|
|
IPPacketFilterPtr FilterPtr;
|
|
FilterPtr = AcquireRefPtr(&FilterRefPtr);
|
|
Action = (*FilterPtr)(IPH, Data, DataSize, NTE->nte_if->if_index,
|
|
INVALID_IF_INDEX, IPADDR_LOCAL, NULL_IP_ADDR);
|
|
ReleaseRefPtr(&FilterRefPtr);
|
|
return ((BOOLEAN) (Action == FORWARD));
|
|
}
|
|
|
|
//** DuumyXmit - Dummy interface transmit handler.
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// Entry: Context - NULL.
|
|
// 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
|
|
__stdcall
|
|
DummyXmit(void *Context, PNDIS_PACKET *PacketArray, uint NumberOfPackets,
|
|
IPAddr Dest, RouteCacheEntry * RCE, void *LinkCtxt)
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(PacketArray);
|
|
UNREFERENCED_PARAMETER(NumberOfPackets);
|
|
UNREFERENCED_PARAMETER(Dest);
|
|
UNREFERENCED_PARAMETER(RCE);
|
|
UNREFERENCED_PARAMETER(LinkCtxt);
|
|
|
|
ASSERT(FALSE);
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
//* DummyXfer - Dummy interface transfer data routine.
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// Entry: Context - NULL.
|
|
// 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
|
|
__stdcall
|
|
DummyXfer(void *Context, NDIS_HANDLE TDContext, uint Dummy, uint Offset, uint BytesToCopy,
|
|
PNDIS_PACKET DestPacket, uint * BytesCopied)
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(TDContext);
|
|
UNREFERENCED_PARAMETER(Dummy);
|
|
UNREFERENCED_PARAMETER(Offset);
|
|
UNREFERENCED_PARAMETER(BytesToCopy);
|
|
UNREFERENCED_PARAMETER(DestPacket);
|
|
UNREFERENCED_PARAMETER(BytesCopied);
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
//* DummyClose - Dummy close routine.
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// Entry: Context - Unused.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
__stdcall
|
|
DummyClose(void *Context)
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
//* DummyInvalidate - .
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// Entry: Context - Unused.
|
|
// RCE - Pointer to RCE to be invalidated.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
__stdcall
|
|
DummyInvalidate(void *Context, RouteCacheEntry * RCE)
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(RCE);
|
|
}
|
|
|
|
//* DummyQInfo - Dummy query information handler.
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// 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
|
|
__stdcall
|
|
DummyQInfo(void *IFContext, TDIObjectID * ID, PNDIS_BUFFER Buffer, uint * Size,
|
|
void *Context)
|
|
{
|
|
UNREFERENCED_PARAMETER(IFContext);
|
|
UNREFERENCED_PARAMETER(ID);
|
|
UNREFERENCED_PARAMETER(Buffer);
|
|
UNREFERENCED_PARAMETER(Size);
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return TDI_INVALID_REQUEST;
|
|
}
|
|
|
|
//* DummySetInfo - Dummy query information handler.
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// 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.
|
|
//
|
|
// Returns: Status of attempt to query information.
|
|
//
|
|
int
|
|
__stdcall
|
|
DummySetInfo(void *IFContext, TDIObjectID * ID, void *Buffer, uint Size)
|
|
{
|
|
UNREFERENCED_PARAMETER(IFContext);
|
|
UNREFERENCED_PARAMETER(ID);
|
|
UNREFERENCED_PARAMETER(Buffer);
|
|
UNREFERENCED_PARAMETER(Size);
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return TDI_INVALID_REQUEST;
|
|
}
|
|
|
|
//* DummyAddAddr - Dummy add address routine.
|
|
//
|
|
// Called at init time when we need to initialize ourselves.
|
|
//
|
|
uint
|
|
__stdcall
|
|
DummyAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask,
|
|
void *Context2)
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(Type);
|
|
UNREFERENCED_PARAMETER(Address);
|
|
UNREFERENCED_PARAMETER(Mask);
|
|
UNREFERENCED_PARAMETER(Context2);
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* DummyDelAddr - Dummy del address routine.
|
|
//
|
|
// Called at init time when we need to initialize ourselves.
|
|
//
|
|
uint
|
|
__stdcall
|
|
DummyDelAddr(void *Context, uint Type, IPAddr Address, IPMask Mask)
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(Type);
|
|
UNREFERENCED_PARAMETER(Address);
|
|
UNREFERENCED_PARAMETER(Mask);
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* DummyGetEList - Dummy get entity list.
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// 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
|
|
__stdcall
|
|
DummyGetEList(void *Context, TDIEntityID * EntityList, uint * Count)
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(EntityList);
|
|
UNREFERENCED_PARAMETER(Count);
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//* DummyDoNdisReq - Dummy send NDIS request
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// Input: Context - Interface context (unused).
|
|
// RT - NDIS Request Type
|
|
// OID - NDIS Request OID
|
|
// Info - Information Buffer.
|
|
// Length - Pointer to size of buffer
|
|
// Needed - Pointer to required size
|
|
// Blocking - Call is Sync or Async
|
|
//
|
|
// Returns Status of attempt to get the info.
|
|
//
|
|
NDIS_STATUS
|
|
__stdcall
|
|
DummyDoNdisReq(void *Context, NDIS_REQUEST_TYPE RT,
|
|
NDIS_OID OID, void *Info, uint Length,
|
|
uint * Needed, BOOLEAN Blocking)
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(RT);
|
|
UNREFERENCED_PARAMETER(OID);
|
|
UNREFERENCED_PARAMETER(Info);
|
|
UNREFERENCED_PARAMETER(Length);
|
|
UNREFERENCED_PARAMETER(Needed);
|
|
UNREFERENCED_PARAMETER(Blocking);
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
#if FFP_SUPPORT
|
|
|
|
// Max number of FFP enabled NIC drivers in the system at any time
|
|
// Note that this serves to limit total cache memory for FFP support
|
|
//
|
|
#define MAXFFPDRVS 8
|
|
|
|
//* IPGetFFPDriverList - Lists unique FFP enabled drivers in the system
|
|
//
|
|
// Called by functions that dispatch requests to FFP enabled drivers
|
|
//
|
|
// Input: arrIF - Array of IFs to reach all FFP enabled drivers
|
|
//
|
|
// Returns: Number of FFP enabled drivers in the system
|
|
//
|
|
uint
|
|
IPGetFFPDriverList(Interface ** arrIF)
|
|
{
|
|
ULONG numIF;
|
|
Interface *IF;
|
|
UINT i;
|
|
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
numIF = 0;
|
|
|
|
// Take a lock to protect the list of all interfaces
|
|
|
|
// Go over the interface list to pick FFP drivers
|
|
for (IF = IFList; IF != NULL; IF = IF->if_next) {
|
|
// Does this interface's driver support FFP ?
|
|
if (IF->if_ffpversion == 0)
|
|
continue;
|
|
|
|
// FFP supported; was driver already picked ?
|
|
for (i = 0; i < numIF; i++) {
|
|
if (IF->if_ffpdriver == arrIF[i]->if_ffpdriver)
|
|
break;
|
|
}
|
|
|
|
if (i == numIF) {
|
|
ASSERT(numIF < MAXFFPDRVS);
|
|
arrIF[numIF++] = IF;
|
|
}
|
|
}
|
|
|
|
// Release lock to protect the list of all interfaces
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
|
|
return numIF;
|
|
}
|
|
|
|
//* IPReclaimRequestMem - Post processing upon request completion
|
|
//
|
|
// ARP calls back upon completion of async requests IP sends ARP
|
|
//
|
|
// Input: pRequestInfo - Points to request IP sends ARP
|
|
//
|
|
// Returns: None
|
|
//
|
|
void
|
|
IPReclaimRequestMem(PVOID pRequestInfo)
|
|
{
|
|
// Decrement ref count, and reclaim memory if it drops to zero
|
|
if (InterlockedDecrement(
|
|
(PLONG) &((ReqInfoBlock *) pRequestInfo)->RequestRefs) == 0) {
|
|
// TCPTRACE(("IPReclaimRequestMem: Freeing mem at pReqInfo = %08X\n",
|
|
// pRequestInfo));
|
|
CTEFreeMem(pRequestInfo);
|
|
}
|
|
}
|
|
|
|
//* IPFlushFFPCaches - Flush all FFP Caches
|
|
//
|
|
// Call ARP to flush FFP caches in layer 2
|
|
//
|
|
// Input: None
|
|
//
|
|
// Returns None
|
|
//
|
|
void
|
|
IPFlushFFPCaches(void)
|
|
{
|
|
Interface *arrIF[MAXFFPDRVS];
|
|
ULONG numIF;
|
|
ReqInfoBlock *pRequestInfo;
|
|
FFPFlushParams *pFlushInfo;
|
|
UINT i;
|
|
|
|
// Check if any requests need to be posted at all
|
|
numIF = IPGetFFPDriverList(arrIF);
|
|
|
|
if (numIF) {
|
|
// Allocate the request block - For General and Request Specific Parts
|
|
pRequestInfo = CTEAllocMemNBoot(sizeof(ReqInfoBlock) + sizeof(FFPFlushParams), '7iCT');
|
|
// TCPTRACE(("IPFlushFFPCaches: Allocated mem at pReqInfo = %08X\n",
|
|
// pRequestInfo));
|
|
|
|
if (pRequestInfo == NULL) {
|
|
return;
|
|
}
|
|
// Prepare the params for the request [ Part common to all requests ]
|
|
pRequestInfo->RequestType = OID_FFP_FLUSH;
|
|
pRequestInfo->ReqCompleteCallback = IPReclaimRequestMem;
|
|
|
|
// Prepare the params for the request [ Part specific to this request ]
|
|
pRequestInfo->RequestLength = sizeof(FFPFlushParams);
|
|
|
|
pFlushInfo = (FFPFlushParams *) pRequestInfo->RequestInfo;
|
|
|
|
pFlushInfo->NdisProtocolType = NDIS_PROTOCOL_ID_TCP_IP;
|
|
|
|
// Assign Initial Ref Count to total num of requests
|
|
pRequestInfo->RequestRefs = numIF;
|
|
|
|
// CTEGetLock(&FFPIFsLock, &lhandle);
|
|
|
|
for (i = 0; i < numIF; i++) {
|
|
// Dispatch the request block to the ARP layer
|
|
ASSERT(arrIF[i]->if_dondisreq != NULL);
|
|
arrIF[i]->if_dondisreq(arrIF[i]->if_lcontext,
|
|
NdisRequestSetInformation,
|
|
OID_FFP_FLUSH,
|
|
pRequestInfo->RequestInfo,
|
|
pRequestInfo->RequestLength,
|
|
NULL, FALSE);
|
|
}
|
|
|
|
// CTEFreeLock(&FFPIFsLock, lhandle);
|
|
}
|
|
}
|
|
|
|
//* IPSetInFFPCaches - Set an entry in all FFP Caches
|
|
//
|
|
// Call ARP to set -ve FFP entries in caches, (or)
|
|
// Invalidate existing +ve or -ve FFP cache entries
|
|
//
|
|
// Input: PacketHeader - Header of the IP Packet
|
|
// Packet - Rest of the IP Packet
|
|
// PacketLength - Length of "Packet" param
|
|
// CacheEntryType - DISCARD (-ve) or INVALID
|
|
//
|
|
// Returns None
|
|
//
|
|
void
|
|
IPSetInFFPCaches(struct IPHeader UNALIGNED * PacketHeader, uchar * Packet,
|
|
uint PacketLength, ulong CacheEntryType)
|
|
{
|
|
Interface *arrIF[MAXFFPDRVS];
|
|
ULONG numIF;
|
|
ReqInfoBlock *pRequestInfo;
|
|
FFPDataParams *pSetInInfo;
|
|
UINT i;
|
|
|
|
// Check if any requests need to be posted at all
|
|
numIF = IPGetFFPDriverList(arrIF);
|
|
|
|
if (numIF) {
|
|
if (PacketLength < sizeof(ULONG)) {
|
|
return;
|
|
}
|
|
// Allocate the request block - For General and Request Specific Parts
|
|
pRequestInfo = CTEAllocMemNBoot(sizeof(ReqInfoBlock) + sizeof(FFPDataParams), '8iCT');
|
|
// TCPTRACE(("IPSetInFFPCaches: Allocated mem at pReqInfo = %08X\n",
|
|
// pRequestInfo));
|
|
|
|
if (pRequestInfo == NULL) {
|
|
return;
|
|
}
|
|
// Prepare the params for the request [ Part common to all requests ]
|
|
pRequestInfo->RequestType = OID_FFP_DATA;
|
|
pRequestInfo->ReqCompleteCallback = IPReclaimRequestMem;
|
|
|
|
// Prepare the params for the request [ Part specific to this request ]
|
|
pRequestInfo->RequestLength = sizeof(FFPDataParams);
|
|
|
|
pSetInInfo = (FFPDataParams *) pRequestInfo->RequestInfo;
|
|
|
|
pSetInInfo->NdisProtocolType = NDIS_PROTOCOL_ID_TCP_IP;
|
|
|
|
pSetInInfo->CacheEntryType = CacheEntryType;
|
|
|
|
pSetInInfo->HeaderSize = sizeof(IPHeader) + sizeof(ULONG);
|
|
RtlCopyMemory(&pSetInInfo->Header, PacketHeader, sizeof(IPHeader));
|
|
pSetInInfo->IpHeader.DwordAfterHeader = *(ULONG *) Packet;
|
|
|
|
// Assign Initial Ref Count to total num of requests
|
|
pRequestInfo->RequestRefs = numIF;
|
|
|
|
// CTEGetLock(&FFPIFsLock, &lhandle);
|
|
|
|
for (i = 0; i < numIF; i++) {
|
|
// Dispatch the request block to the ARP layer
|
|
ASSERT(arrIF[i]->if_dondisreq != NULL);
|
|
arrIF[i]->if_dondisreq(arrIF[i]->if_lcontext,
|
|
NdisRequestSetInformation,
|
|
OID_FFP_DATA,
|
|
pRequestInfo->RequestInfo,
|
|
pRequestInfo->RequestLength,
|
|
NULL, FALSE);
|
|
}
|
|
|
|
// CTEFreeLock(&FFPIFsLock, lhandle);
|
|
}
|
|
}
|
|
|
|
//* IPStatsFromFFPCaches - Sum Stats from all FFP Caches
|
|
//
|
|
// Call ARP to get FFP Stats in layer 2
|
|
//
|
|
// Input: Pointer to the buffer that is filled with statistics
|
|
//
|
|
// Returns None
|
|
//
|
|
void
|
|
IPStatsFromFFPCaches(FFPDriverStats * pCumulStats)
|
|
{
|
|
Interface *arrIF[MAXFFPDRVS];
|
|
ULONG numIF;
|
|
UINT i;
|
|
FFPDriverStats DriverStatsInfo =
|
|
{
|
|
NDIS_PROTOCOL_ID_TCP_IP,
|
|
0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
RtlZeroMemory(pCumulStats, sizeof(FFPDriverStats));
|
|
|
|
numIF = IPGetFFPDriverList(arrIF);
|
|
if (numIF) {
|
|
// CTEGetLock(&FFPIFsLock, &lhandle);
|
|
|
|
for (i = 0; i < numIF; i++) {
|
|
// Dispatch the request block to the ARP layer
|
|
ASSERT(arrIF[i]->if_dondisreq != NULL);
|
|
if (arrIF[i]->if_dondisreq(arrIF[i]->if_lcontext,
|
|
NdisRequestQueryInformation,
|
|
OID_FFP_DRIVER_STATS,
|
|
&DriverStatsInfo,
|
|
sizeof(FFPDriverStats),
|
|
NULL, TRUE) == NDIS_STATUS_SUCCESS) {
|
|
// Consolidate results from all drivers
|
|
pCumulStats->PacketsForwarded += DriverStatsInfo.PacketsForwarded;
|
|
pCumulStats->OctetsForwarded += DriverStatsInfo.OctetsForwarded;
|
|
|
|
pCumulStats->PacketsDiscarded += DriverStatsInfo.PacketsDiscarded;
|
|
pCumulStats->OctetsDiscarded += DriverStatsInfo.OctetsDiscarded;
|
|
|
|
pCumulStats->PacketsIndicated += DriverStatsInfo.PacketsIndicated;
|
|
pCumulStats->OctetsIndicated += DriverStatsInfo.OctetsIndicated;
|
|
}
|
|
}
|
|
|
|
// CTEFreeLock(&FFPIFsLock, lhandle);
|
|
}
|
|
}
|
|
|
|
#endif // if FFP_SUPPORT
|
|
|
|
//* DerefIF - Dereference an interface.
|
|
//
|
|
// Called when we need to dereference an interface. We decrement the
|
|
// refcount, and if it goes to zero we signal whoever is blocked on
|
|
// it.
|
|
//
|
|
// Input: IF - Interfaec to be dereferenced.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
#pragma optimize("", off)
|
|
void
|
|
DerefIF(Interface * IF)
|
|
{
|
|
uint Original;
|
|
|
|
Original = DEREFERENCE_IF(IF);
|
|
|
|
if (Original != 1) {
|
|
return;
|
|
} else {
|
|
// We just decremented the last reference. Wake whoever is
|
|
// blocked on it.
|
|
ASSERT(IF->if_block != NULL);
|
|
CTESignal(IF->if_block, NDIS_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
//* LockedDerefIF - Dereference an interface w/RouteTableLock held.
|
|
//
|
|
// Called when we need to dereference an interface. We decrement the
|
|
// refcount, and if it goes to zero we signal whoever is blocked on
|
|
// it. The difference here is that we assume the caller already holds
|
|
// the RouteTableLock.
|
|
//
|
|
// Input: IF - Interfaec to be dereferenced.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
LockedDerefIF(Interface * IF)
|
|
{
|
|
LOCKED_DEREFERENCE_IF(IF);
|
|
|
|
if (IF->if_refcount != 0) {
|
|
return;
|
|
} else {
|
|
// We just decremented the last reference. Wake whoever is
|
|
// blocked on it.
|
|
ASSERT(IF->if_block != NULL);
|
|
CTESignal(IF->if_block, NDIS_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
#pragma optimize("", on)
|
|
|
|
//* DerefLink - Dereference the Link
|
|
//
|
|
// Called when we need to dereference a link. We decrement the
|
|
// refcount, and if it goes to zero we free the link
|
|
//
|
|
// Input: Link - Link to be dereferenced.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
DerefLink(LinkEntry * Link)
|
|
{
|
|
uint Original;
|
|
|
|
Original = CTEInterlockedExchangeAdd(&Link->link_refcount, -1);
|
|
|
|
if (Original != 1) {
|
|
return;
|
|
} else {
|
|
// We just decremented the last reference.
|
|
// Call CloseLink to Notify lower layer that link is going down
|
|
|
|
ASSERT(Link->link_if);
|
|
ASSERT(Link->link_if->if_closelink);
|
|
|
|
#if DBG
|
|
// P2MP stuff still needs to be cooked
|
|
{
|
|
Interface *IF = Link->link_if;
|
|
LinkEntry *tmpLink = IF->if_link;
|
|
|
|
while (tmpLink) {
|
|
if (tmpLink == Link) {
|
|
// freeing the Link without cleaning up??
|
|
DbgBreakPoint();
|
|
}
|
|
tmpLink = tmpLink->link_next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
(*(Link->link_if->if_closelink)) (Link->link_if->if_lcontext, Link->link_arpctxt);
|
|
// Free the link
|
|
CTEFreeMem(Link);
|
|
}
|
|
}
|
|
|
|
//** AddrOnIF - Check to see if a given address is local to an IF
|
|
//
|
|
// Called when we want to see if a given address is a valid local address
|
|
// for an interface. We walk down the chain of NTEs in the interface, and
|
|
// see if we get a match. We assume the caller holds the RouteTableLock
|
|
// at this point.
|
|
//
|
|
// Input: IF - Interface to check.
|
|
// Addr - Address to check.
|
|
//
|
|
// Returns: TRUE if Addr is an address for IF, FALSE otherwise.
|
|
//
|
|
uint
|
|
AddrOnIF(Interface * IF, IPAddr Addr)
|
|
{
|
|
NetTableEntry *NTE;
|
|
|
|
NTE = IF->if_nte;
|
|
while (NTE != NULL) {
|
|
if ((NTE->nte_flags & NTE_VALID) && IP_ADDR_EQUAL(NTE->nte_addr, Addr))
|
|
return TRUE;
|
|
else
|
|
NTE = NTE->nte_ifnext;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//** BestNTEForIF - Find the 'best match' NTE on a given interface.
|
|
//
|
|
// This is a utility function that takes an address and tries to find the
|
|
// 'best match' NTE on a given interface. This is really only useful when we
|
|
// have multiple IP addresses on a single interface.
|
|
//
|
|
// Input: Address - Source address of packet.
|
|
// IF - Pointer to IF to be searched.
|
|
// NoTransientAddr - Filter/don't filter out transient address.
|
|
// Returns: The 'best match' NTE.
|
|
//
|
|
NetTableEntry *
|
|
BestNTEForIF(IPAddr Address, Interface * IF, BOOLEAN NoTransientAddr)
|
|
{
|
|
NetTableEntry *CurrentNTE, *FoundNTE;
|
|
uint i;
|
|
|
|
if (IF->if_nte != NULL) {
|
|
// Walk the list of NTEs, looking for a valid one.
|
|
CurrentNTE = IF->if_nte;
|
|
FoundNTE = NULL;
|
|
do {
|
|
if (CurrentNTE->nte_flags & NTE_VALID) {
|
|
if (IP_ADDR_EQUAL(Address & CurrentNTE->nte_mask,
|
|
(CurrentNTE->nte_addr &
|
|
CurrentNTE->nte_mask))) {
|
|
|
|
// If the address is a transient one and
|
|
// if caller wants us to check if non transient
|
|
// address is available then skip this address.
|
|
// However, in the event no non-transient address
|
|
// is available this will be returned anyway.
|
|
|
|
if (NoTransientAddr &&
|
|
(CurrentNTE->nte_flags & NTE_TRANSIENT_ADDR)) {
|
|
FoundNTE = CurrentNTE;
|
|
} else {
|
|
return CurrentNTE;
|
|
}
|
|
|
|
} else if (FoundNTE == NULL) {
|
|
FoundNTE = CurrentNTE;
|
|
}
|
|
|
|
|
|
}
|
|
CurrentNTE = CurrentNTE->nte_ifnext;
|
|
} while (CurrentNTE != NULL);
|
|
|
|
// If we found a match, or we didn't and the destination is not
|
|
// a broadcast, return the result. We have special case code to
|
|
// handle broadcasts, since the interface doesn't really matter there.
|
|
if (FoundNTE != NULL || (!IP_ADDR_EQUAL(Address, IP_LOCAL_BCST) &&
|
|
!IP_ADDR_EQUAL(Address, IP_ZERO_BCST))) {
|
|
return FoundNTE;
|
|
}
|
|
}
|
|
// An 'anonymous' I/F, or the address we're reaching is a broadcast and the
|
|
// first interface has no address. Find a valid (non-loopback, non-null ip,
|
|
// non-uni) address.
|
|
for (i = 0; i < NET_TABLE_SIZE; i++) {
|
|
NetTableEntry *NetTableList = NewNetTableList[i];
|
|
for (CurrentNTE = NetTableList; CurrentNTE != NULL;
|
|
CurrentNTE = CurrentNTE->nte_next) {
|
|
if (CurrentNTE != LoopNTE &&
|
|
(CurrentNTE->nte_flags & NTE_VALID) &&
|
|
!((CurrentNTE->nte_if->if_flags & IF_FLAGS_NOIPADDR) && IP_ADDR_EQUAL(CurrentNTE->nte_addr, NULL_IP_ADDR)) &&
|
|
!(CurrentNTE->nte_if->if_flags & IF_FLAGS_UNI)) {
|
|
return CurrentNTE;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
|
|
}
|
|
|
|
//** IsBCastonNTE - Determine if the specified addr. is a bcast on a spec. NTE.
|
|
//
|
|
// This routine is called when we need to know if an address is a broadcast
|
|
// on a particular net. We check in the order we expect to be most common - a
|
|
// subnet bcast, an all ones broadcast, and then an all subnets broadcast. We
|
|
// return the type of broadcast it is, or return DEST_LOCAL if it's not a
|
|
// broadcast.
|
|
//
|
|
// Entry: Address - Address in question.
|
|
// NTE - NetTableEntry to check Address against.
|
|
//
|
|
// Returns: Type of broadcast.
|
|
//
|
|
uchar
|
|
IsBCastOnNTE(IPAddr Address, NetTableEntry * NTE)
|
|
{
|
|
IPMask Mask;
|
|
IPAddr BCastAddr;
|
|
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
|
|
BCastAddr = NTE->nte_if->if_bcast;
|
|
Mask = NTE->nte_mask;
|
|
|
|
if (Mask != 0xFFFFFFFF) {
|
|
if (IP_ADDR_EQUAL(Address,
|
|
(NTE->nte_addr & Mask) | (BCastAddr & ~Mask)))
|
|
return DEST_SN_BCAST;
|
|
}
|
|
// See if it's an all subnet's broadcast.
|
|
if (!CLASSD_ADDR(Address)) {
|
|
Mask = IPNetMask(Address);
|
|
|
|
if (IP_ADDR_EQUAL(Address,
|
|
(NTE->nte_addr & Mask) | (BCastAddr & ~Mask)))
|
|
return DEST_BCAST;
|
|
} else {
|
|
// This is a class D address. If we're allowed to receive
|
|
// mcast datagrams, check our list.
|
|
|
|
return DEST_MCAST;
|
|
}
|
|
|
|
// A global bcast is certainly a bcast on this net.
|
|
if (IP_ADDR_EQUAL(Address, BCastAddr))
|
|
return DEST_BCAST;
|
|
|
|
} else if (RefPtrValid(&DHCPRefPtr)) {
|
|
if (AcquireRefPtr(&DHCPRefPtr) == NTE) {
|
|
|
|
BCastAddr = NTE->nte_if->if_bcast;
|
|
ReleaseRefPtr(&DHCPRefPtr);
|
|
|
|
if ((IP_ADDR_EQUAL(Address, BCastAddr))) {
|
|
return (DEST_BCAST);
|
|
}
|
|
} else {
|
|
ReleaseRefPtr(&DHCPRefPtr);
|
|
}
|
|
}
|
|
return DEST_LOCAL;
|
|
}
|
|
|
|
//** InvalidSourceAddress - Check to see if a source address is invalid.
|
|
//
|
|
// This function takes an input address and checks to see if it is valid
|
|
// if used as the source address of an incoming packet. An address is invalid
|
|
// if it's 0, -1, a Class D or Class E address, is a net or subnet broadcast,
|
|
// or has a 0 subnet or host part.
|
|
//
|
|
// Input: Address - Address to be check.
|
|
//
|
|
// Returns: FALSE if the address is not invalid, TRUE if it is invalid.
|
|
//
|
|
uint
|
|
InvalidSourceAddress(IPAddr Address)
|
|
{
|
|
NetTableEntry *NTE; // Pointer to current NTE.
|
|
IPMask Mask; // Mask for address.
|
|
IPAddr MaskedAddress;
|
|
IPAddr LocalAddress;
|
|
uint i;
|
|
|
|
if (!CLASSD_ADDR(Address) &&
|
|
!CLASSE_ADDR(Address) &&
|
|
!IP_ADDR_EQUAL(Address, IP_ZERO_BCST) &&
|
|
!IP_ADDR_EQUAL(Address, IP_LOCAL_BCST)
|
|
) {
|
|
// It's not an obvious broadcast. See if it's an all subnets
|
|
// broadcast, or has a zero host part.
|
|
Mask = IPNetMask(Address);
|
|
MaskedAddress = Address & Mask;
|
|
|
|
if (!IP_ADDR_EQUAL(Address, MaskedAddress) &&
|
|
!IP_ADDR_EQUAL(Address, (MaskedAddress | ~Mask))
|
|
) {
|
|
// It's not an all subnet's broadcast, and it has a non-zero
|
|
// host/subnet part. Walk our local IP addresses, and see if it's
|
|
// a subnet broadcast.
|
|
for (i = 0; i < NET_TABLE_SIZE; i++) {
|
|
NetTableEntry *NetTableList = NewNetTableList[i];
|
|
NTE = NetTableList;
|
|
while (NTE) {
|
|
|
|
LocalAddress = NTE->nte_addr;
|
|
|
|
if ((NTE->nte_flags & NTE_VALID) &&
|
|
!IP_LOOPBACK(LocalAddress)) {
|
|
|
|
Mask = NTE->nte_mask;
|
|
MaskedAddress = LocalAddress & Mask;
|
|
|
|
if (!IP_ADDR_EQUAL(Mask, HOST_MASK)) {
|
|
if (IP_ADDR_EQUAL(Address, MaskedAddress) ||
|
|
IP_ADDR_EQUAL(Address,
|
|
(MaskedAddress |
|
|
(NTE->nte_if->if_bcast & ~Mask)))) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
NTE = NTE->nte_next;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// 8 regions of 31 cache elements.
|
|
// Each region is indexed by the 3 most significant bits of the IP address.
|
|
// Each cache element within a region is indexed by a hash of the IP address.
|
|
// Each cache element is composed of 29 least significant bits of the IP
|
|
// address plus the three bit address type code.
|
|
// (31 is prime and works well with our hash.)
|
|
//
|
|
#define ATC_BITS 3
|
|
#define ATC_ELEMENTS_PER_REGION 31
|
|
|
|
#define ATC_REGIONS (1 << ATC_BITS)
|
|
#define ATC_CODE_MASK (ULONG32)(ATC_REGIONS - 1)
|
|
#define ATC_ADDR_MASK (ULONG32)(~ATC_CODE_MASK)
|
|
|
|
// sanity check for 3 bits of address type code
|
|
C_ASSERT(ATC_REGIONS == 8);
|
|
C_ASSERT(ATC_CODE_MASK == 0x00000007);
|
|
C_ASSERT(ATC_ADDR_MASK == 0xFFFFFFF8);
|
|
|
|
// Each cache element is 32 bits to support atomic reading and writing.
|
|
//
|
|
ULONG32 AddrTypeCache [ATC_REGIONS * ATC_ELEMENTS_PER_REGION];
|
|
|
|
#if DBG
|
|
ULONG DbgAddrTypeCacheHits;
|
|
ULONG DbgAddrTypeCacheMisses;
|
|
ULONG DbgAddrTypeCacheCollisions;
|
|
ULONG DbgAddrTypeCacheFlushes;
|
|
ULONG DbgAddrTypeCacheNoUpdates;
|
|
ULONG DbgAddrTypeCacheLastNoUpdateDestType;
|
|
#endif
|
|
|
|
// The following type codes must fit within ATC_BITS of information.
|
|
//
|
|
typedef enum _ADDRESS_TYPE_CODE {
|
|
ATC_LOCAL = 0,
|
|
ATC_BCAST,
|
|
ATC_MCAST,
|
|
ATC_REMOTE,
|
|
ATC_REMOTE_BCAST,
|
|
ATC_REMOTE_MCAST,
|
|
ATC_SUBNET_BCAST,
|
|
ATC_NUM_CODES
|
|
} ADDRESS_TYPE_CODE;
|
|
|
|
// The following array is indexed by ADDRESS_TYPE_CODE values.
|
|
//
|
|
const char MapAddrTypeCodeToDestType [] = {
|
|
DEST_LOCAL,
|
|
DEST_BCAST,
|
|
DEST_MCAST,
|
|
DEST_REMOTE,
|
|
DEST_REM_BCAST,
|
|
DEST_REM_MCAST,
|
|
DEST_SN_BCAST,
|
|
};
|
|
|
|
//** ComputeAddrTypeCacheIndex - Given an IP address, compute the index
|
|
// of its corresponding entry in the address type cache.
|
|
//
|
|
// Input: Address - IP Address to compute the index of.
|
|
//
|
|
// Returns: Valid index into the address type cache.
|
|
//
|
|
__forceinline
|
|
ULONG
|
|
ComputeAddrTypeCacheIndex(IPAddr Address)
|
|
{
|
|
ULONG Region;
|
|
ULONG Offset;
|
|
ULONG Index;
|
|
|
|
// Locate the region of the cache where this Address would reside.
|
|
//
|
|
Region = Address >> (32 - ATC_BITS);
|
|
ASSERT(Region < ATC_REGIONS);
|
|
|
|
// Locate the offset into the region where this address would reside.
|
|
// This is done by hashing the address.
|
|
//
|
|
Offset = (1103515245 * Address + 12345) % ATC_ELEMENTS_PER_REGION;
|
|
|
|
// Compute the cache index and return it.
|
|
//
|
|
Index = (Region * ATC_ELEMENTS_PER_REGION) + Offset;
|
|
|
|
ASSERT(Index < (sizeof(AddrTypeCache) / sizeof(AddrTypeCache[0])));
|
|
|
|
return Index;
|
|
}
|
|
|
|
//** AddrTypeCacheFlush - Flush the cache entry associated with an address.
|
|
//
|
|
// Input: Address - Address to remove from the cache.
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
AddrTypeCacheFlush(IPAddr Address)
|
|
{
|
|
ULONG CacheIndex;
|
|
|
|
CacheIndex = ComputeAddrTypeCacheIndex(Address);
|
|
|
|
AddrTypeCache [CacheIndex] = 0;
|
|
|
|
#if DBG
|
|
DbgAddrTypeCacheFlushes++;
|
|
#endif
|
|
}
|
|
|
|
//** AddrTypeCacheLookup - Lookup an address from the address type cache.
|
|
//
|
|
// Input: Address - Address to be lookup.
|
|
// Output: CacheIndex - Pointer to cache index corresponding to the Address.
|
|
// DestType - Pointer to destination type to be filled in if
|
|
// the address is found in the cache.
|
|
//
|
|
// Returns: TRUE if the address was found in the cache.
|
|
//
|
|
// N.B. The output parameter DestType is only initialized if TRUE is returned.
|
|
//
|
|
__forceinline
|
|
BOOLEAN
|
|
AddrTypeCacheLookup(IPAddr Address, ULONG *CacheIndex, uchar *DestType)
|
|
{
|
|
ULONG32 CacheValue;
|
|
|
|
// Read the value of the cache corresponding to this address.
|
|
//
|
|
*CacheIndex = ComputeAddrTypeCacheIndex(Address);
|
|
CacheValue = AddrTypeCache [*CacheIndex];
|
|
|
|
// If the cached value is non-zero and matches the relevent portion of
|
|
// the address, then get the type code and translate it to the proper
|
|
// destination type.
|
|
//
|
|
if ((CacheValue != 0) &&
|
|
(((Address << ATC_BITS) ^ CacheValue) & ATC_ADDR_MASK) == 0) {
|
|
|
|
ADDRESS_TYPE_CODE TypeCode = CacheValue & ATC_CODE_MASK;
|
|
|
|
ASSERT(TypeCode < ATC_NUM_CODES);
|
|
*DestType = MapAddrTypeCodeToDestType[TypeCode];
|
|
|
|
#if DBG
|
|
DbgAddrTypeCacheHits++;
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
#if DBG
|
|
DbgAddrTypeCacheMisses++;
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//** AddrTypeCacheUpdate - Add or update the destination type for an Address.
|
|
// in the cache.
|
|
//
|
|
// Input: Address - Address to be add or update.
|
|
// CacheIndex - Cache index corresponding to the Address.
|
|
// DestType - Destination type to cache for the Address.
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
__forceinline
|
|
void
|
|
AddrTypeCacheUpdate(IPAddr Address, ULONG CacheIndex, uchar DestType)
|
|
{
|
|
ADDRESS_TYPE_CODE TypeCode = ATC_LOCAL;
|
|
BOOLEAN Update = TRUE;
|
|
|
|
ASSERT(CacheIndex < (sizeof(AddrTypeCache) / sizeof(AddrTypeCache[0])));
|
|
|
|
switch (DestType) {
|
|
case DEST_LOCAL:
|
|
TypeCode = ATC_LOCAL;
|
|
break;
|
|
case DEST_BCAST:
|
|
TypeCode = ATC_BCAST;
|
|
break;
|
|
case DEST_MCAST:
|
|
TypeCode = ATC_MCAST;
|
|
break;
|
|
case DEST_REMOTE:
|
|
TypeCode = ATC_REMOTE;
|
|
break;
|
|
case DEST_REM_BCAST:
|
|
TypeCode = ATC_REMOTE_BCAST;
|
|
break;
|
|
case DEST_REM_MCAST:
|
|
TypeCode = ATC_REMOTE_MCAST;
|
|
break;
|
|
case DEST_SN_BCAST:
|
|
TypeCode = ATC_SUBNET_BCAST;
|
|
break;
|
|
default:
|
|
Update = FALSE;
|
|
#if DBG
|
|
DbgAddrTypeCacheNoUpdates++;
|
|
DbgAddrTypeCacheLastNoUpdateDestType = DestType;
|
|
#endif
|
|
}
|
|
|
|
if (Update) {
|
|
#if DBG
|
|
ULONG32 CacheValue = AddrTypeCache [CacheIndex];
|
|
|
|
if (CacheValue != 0) {
|
|
DbgAddrTypeCacheCollisions++;
|
|
}
|
|
#endif
|
|
|
|
AddrTypeCache [CacheIndex] = (Address << ATC_BITS) | TypeCode;
|
|
}
|
|
}
|
|
|
|
//** GetAddrType - Return the destination type of a specified address.
|
|
//
|
|
// Input: Address - Address to get the destination type of.
|
|
//
|
|
// Returns: Destination type.
|
|
//
|
|
uchar
|
|
GetAddrType(IPAddr Address)
|
|
{
|
|
ULONG CacheIndex;
|
|
NetTableEntry *NTE; // Pointer to current NTE.
|
|
IPMask Mask; // Mask for address.
|
|
IPMask SNMask;
|
|
uint i;
|
|
uchar Result; // Result of broadcast check.
|
|
|
|
// Check the cache and return if we got a hit.
|
|
//
|
|
if (AddrTypeCacheLookup(Address, &CacheIndex, &Result)) {
|
|
return Result;
|
|
}
|
|
|
|
// We don't cache, nor do we need to cache, these types of invalid
|
|
// addresses.
|
|
//
|
|
if (CLASSE_ADDR(Address)) {
|
|
return DEST_INVALID;
|
|
}
|
|
|
|
// See if it's one of our local addresses, or a broadcast
|
|
// on a local address.
|
|
// optimize it for the DEST_LOCAL case
|
|
//
|
|
for (NTE = NewNetTableList[NET_TABLE_HASH(Address)];
|
|
NTE; NTE = NTE->nte_next) {
|
|
|
|
if (IP_ADDR_EQUAL(NTE->nte_addr, Address) &&
|
|
(NTE->nte_flags & NTE_VALID) &&
|
|
!((IP_ADDR_EQUAL(Address, NULL_IP_ADDR) && (NTE->nte_if->if_flags & IF_FLAGS_NOIPADDR)))) {
|
|
Result = DEST_LOCAL;
|
|
goto gat_exit;
|
|
}
|
|
}
|
|
|
|
// go thru the whole table for other cases
|
|
//
|
|
for (i = 0; i < NET_TABLE_SIZE; i++) {
|
|
for (NTE = NewNetTableList[i]; NTE; NTE = NTE->nte_next) {
|
|
|
|
if (!(NTE->nte_flags & NTE_VALID)) {
|
|
continue;
|
|
}
|
|
|
|
if ((Result = IsBCastOnNTE(Address, NTE)) != DEST_LOCAL) {
|
|
goto gat_exit;
|
|
}
|
|
|
|
// See if the destination has a valid host part.
|
|
SNMask = NTE->nte_mask;
|
|
if (IP_ADDR_EQUAL(Address & SNMask, NTE->nte_addr & SNMask)) {
|
|
// On this subnet. See if the host part is invalid.
|
|
|
|
if (IP_ADDR_EQUAL(Address & SNMask, Address)) {
|
|
Result = DEST_INVALID; // Invalid 0 host part.
|
|
goto gat_exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// It's not a local address, see if it's loopback.
|
|
if (IP_LOOPBACK(Address)) {
|
|
Result = DEST_LOCAL;
|
|
goto gat_exit;
|
|
}
|
|
|
|
// If we're doing IGMP, see if it's a Class D address. If it is,
|
|
// return that.
|
|
if (CLASSD_ADDR(Address)) {
|
|
if (IGMPLevel != 0) {
|
|
Result = DEST_REM_MCAST;
|
|
goto gat_exit;
|
|
} else {
|
|
Result = DEST_INVALID;
|
|
goto gat_exit;
|
|
}
|
|
}
|
|
Mask = IPNetMask(Address);
|
|
|
|
// Now check remote broadcast. When we get here we know that the
|
|
// address is not a global broadcast, a subnet broadcast for a subnet
|
|
// of which we're a member, or an all-subnets broadcast for a net of
|
|
// which we're a member. Since we're avoiding making assumptions about
|
|
// all subnet of a net having the same mask, we can't really check for
|
|
// a remote subnet broadcast. We'll use the net mask and see if it's
|
|
// a remote all-subnet's broadcast.
|
|
if (IP_ADDR_EQUAL(Address, (Address & Mask) | (IP_LOCAL_BCST & ~Mask))) {
|
|
Result = DEST_REM_BCAST;
|
|
goto gat_exit;
|
|
}
|
|
|
|
// Check for invalid 0 parts. All we can do from here is see if he's
|
|
// sending to a remote net with all zero subnet and host parts. We
|
|
// can't check to see if he's sending to a remote subnet with an all
|
|
// zero host part.
|
|
if (IP_ADDR_EQUAL(Address, NULL_IP_ADDR)) {
|
|
Result = DEST_INVALID;
|
|
goto gat_exit;
|
|
}
|
|
|
|
#if DBG
|
|
if (IP_ADDR_EQUAL(Address, Address & Mask)) {
|
|
//This is a remote address with null host part per classfull address
|
|
//But may be a supernetted address, where the prefix len is less than the
|
|
//class mask prefix len for the metid.
|
|
//We should let this address go out.
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," GAT: zero host part %x?\n", Address));
|
|
}
|
|
#endif
|
|
// Must be remote.
|
|
Result = DEST_REMOTE;
|
|
|
|
gat_exit:
|
|
|
|
AddrTypeCacheUpdate(Address, CacheIndex, Result);
|
|
|
|
return Result;
|
|
}
|
|
|
|
//** GetLocalNTE - Get the local NTE for an incoming packet.
|
|
//
|
|
// Called during receive processing to find a matching NTE for a packet.
|
|
// First we check against the NTE we received it on, then against any NTE.
|
|
//
|
|
// Input: Address - The dest. address of the packet.
|
|
// NTE - Pointer to NTE packet was received on - filled in on
|
|
// exit w/correct NTE.
|
|
//
|
|
// Returns: DEST_LOCAL if the packet is destined for this host,
|
|
// DEST_REMOTE if it needs to be routed,
|
|
// DEST_SN_BCAST or DEST_BCAST if it's some sort of a broadcast.
|
|
//
|
|
uchar
|
|
GetLocalNTE(IPAddr Address, NetTableEntry ** NTE)
|
|
{
|
|
NetTableEntry *LocalNTE = *NTE;
|
|
IPMask Mask;
|
|
uchar Result;
|
|
uint i;
|
|
Interface *LocalIF;
|
|
NetTableEntry *OriginalNTE;
|
|
|
|
// Quick check to see if it is for the NTE it came in on (the common case).
|
|
if (IP_ADDR_EQUAL(Address, LocalNTE->nte_addr) &&
|
|
(LocalNTE->nte_flags & NTE_VALID))
|
|
return DEST_LOCAL; // For us, just return.
|
|
|
|
// Now check to see if it's a broadcast of some sort on the interface it
|
|
// came in on.
|
|
if ((Result = IsBCastOnNTE(Address, LocalNTE)) != DEST_LOCAL)
|
|
return Result;
|
|
//Is this a mcast on a loop interface
|
|
if ((LocalNTE == LoopNTE) && CLASSD_ADDR(Address)) {
|
|
return DEST_MCAST;
|
|
}
|
|
// The common cases failed us. Loop through the NetTable and see if
|
|
// it is either a valid local address or is a broadcast on one of the NTEs
|
|
// on the incoming interface. We won't check the NTE we've already looked
|
|
// at. We look at all NTEs, including the loopback NTE, because a loopback
|
|
// frame could come through here. Also, frames from ourselves to ourselves
|
|
// will come in on the loopback NTE.
|
|
|
|
i = 0;
|
|
LocalIF = LocalNTE->nte_if;
|
|
OriginalNTE = LocalNTE;
|
|
// optimize it for the DEST_LOCAL case
|
|
LocalNTE = NewNetTableList[NET_TABLE_HASH(Address)];
|
|
while (LocalNTE) {
|
|
if (LocalNTE != OriginalNTE) {
|
|
if (IP_ADDR_EQUAL(Address, LocalNTE->nte_addr) &&
|
|
(LocalNTE->nte_flags & NTE_VALID) &&
|
|
!((IP_ADDR_EQUAL(Address, NULL_IP_ADDR) && (LocalNTE->nte_if->if_flags & IF_FLAGS_NOIPADDR)))) {
|
|
*NTE = LocalNTE;
|
|
return DEST_LOCAL; // For us, just return.
|
|
|
|
}
|
|
}
|
|
LocalNTE = LocalNTE->nte_next;
|
|
|
|
}
|
|
|
|
// go thru the whole table for other cases
|
|
|
|
for (i = 0; i < NET_TABLE_SIZE; i++) {
|
|
NetTableEntry *NetTableList = NewNetTableList[i];
|
|
LocalNTE = NetTableList;
|
|
while (LocalNTE) {
|
|
if (LocalNTE != OriginalNTE) {
|
|
|
|
// If this NTE is on the same interface as the NTE it arrived on,
|
|
// see if it's a broadcast.
|
|
if (LocalIF == LocalNTE->nte_if)
|
|
if ((Result = IsBCastOnNTE(Address, LocalNTE)) != DEST_LOCAL) {
|
|
*NTE = LocalNTE;
|
|
return Result;
|
|
}
|
|
}
|
|
LocalNTE = LocalNTE->nte_next;
|
|
|
|
}
|
|
}
|
|
|
|
// It's not a local address, see if it's loopback.
|
|
if (IP_LOOPBACK(Address)) {
|
|
*NTE = LoopNTE;
|
|
return DEST_LOCAL;
|
|
}
|
|
// If it's a class D address and we're receiveing multicasts, handle it
|
|
// here.
|
|
if (CLASSD_ADDR(Address)) {
|
|
if (IGMPLevel != 0)
|
|
return DEST_REM_MCAST;
|
|
else
|
|
return DEST_INVALID;
|
|
}
|
|
// It's not local. Check to see if maybe it's a net broadcast for a net
|
|
// of which we're not a member. If so, return remote bcast. We can't check
|
|
// for subnet broadcast of subnets for which we're not a member, since we're
|
|
// not making assumptions about all subnets of a single net having the
|
|
// same mask. If we're here it's not a subnet broadcast for a net of which
|
|
// we're a member, so we don't know a subnet mask for it. We'll just use
|
|
// the net mask.
|
|
Mask = IPNetMask(Address);
|
|
if (((*NTE)->nte_flags & NTE_VALID) &&
|
|
(IP_ADDR_EQUAL(Address, (Address & Mask) |
|
|
((*NTE)->nte_if->if_bcast & ~Mask))))
|
|
return DEST_REM_BCAST;
|
|
|
|
// If it's to the 0 address, or a Class E address, or has an all-zero
|
|
// subnet and net part, it's invalid.
|
|
|
|
if (IP_ADDR_EQUAL(Address, IP_ZERO_BCST) ||
|
|
IP_ADDR_EQUAL(Address, (Address & Mask)) ||
|
|
CLASSE_ADDR(Address))
|
|
return DEST_INVALID;
|
|
|
|
// If we're DHCPing the interface on which this came in we'll accept this.
|
|
// If it came in as a broadcast a check in IPRcv() will reject it. If it's
|
|
// a unicast to us we'll pass it up.
|
|
if ((*NTE)->nte_flags & NTE_DHCP) {
|
|
ASSERT(!((*NTE)->nte_flags & NTE_VALID));
|
|
return DEST_LOCAL;
|
|
}
|
|
return DEST_REMOTE;
|
|
}
|
|
|
|
//** IsRouteICMP - This function is used by Router Discovery to determine
|
|
// how we learned about the route. We are not allowed to update or timeout
|
|
// routes that were not learned about via icmp. If the route is new then
|
|
// we treat it as icmp and add a new entry.
|
|
// Input: Dest - Destination to search for.
|
|
// Mask - Mask for destination.
|
|
// FirstHop - FirstHop to Dest.
|
|
// OutIF - Pointer to outgoing interface structure.
|
|
//
|
|
// Returns: TRUE if learned via ICMP, FALSE otherwise.
|
|
//
|
|
uint
|
|
IsRouteICMP(IPAddr Dest, IPMask Mask, IPAddr FirstHop, Interface * OutIF)
|
|
{
|
|
RouteTableEntry *RTE;
|
|
RouteTableEntry *TempRTE;
|
|
|
|
RTE = FindSpecificRTE(Dest, Mask, FirstHop, OutIF, &TempRTE, FALSE);
|
|
|
|
if (RTE == NULL)
|
|
return (TRUE);
|
|
|
|
if (RTE->rte_proto == IRE_PROTO_ICMP) {
|
|
return (TRUE);
|
|
} else {
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
UpdateDeadGWState( )
|
|
{
|
|
uint Active = 0;
|
|
uint Configured = 0;
|
|
RouteTableEntry* RTE;
|
|
RTE = GetDefaultGWs(&RTE);
|
|
while (RTE) {
|
|
++Configured;
|
|
if (RTE->rte_flags & RTE_VALID)
|
|
++Active;
|
|
RTE = RTE->rte_next;
|
|
}
|
|
DefGWActive = Active;
|
|
DefGWConfigured = Configured;
|
|
}
|
|
|
|
//* ValidateDefaultGWs - Mark all default gateways as valid.
|
|
//
|
|
// Called to one or all of our default gateways as up. The caller specifies
|
|
// the IP address of the one to mark as up, or NULL_IP_ADDR if they're all
|
|
// supposed to be marked up. We return a count of how many we marked as
|
|
// valid.
|
|
//
|
|
// Input: IP address of G/W to mark as up.
|
|
//
|
|
// Returns: Count of gateways marked as up.
|
|
//
|
|
uint
|
|
ValidateDefaultGWs(IPAddr Addr)
|
|
{
|
|
RouteTableEntry *RTE;
|
|
uint Count = 0;
|
|
uint Now = CTESystemUpTime() / 1000L;
|
|
|
|
RTE = GetDefaultGWs(&RTE);
|
|
|
|
while (RTE != NULL) {
|
|
if (RTE->rte_mask == DEFAULT_MASK && !(RTE->rte_flags & RTE_VALID) &&
|
|
(IP_ADDR_EQUAL(Addr, NULL_IP_ADDR) ||
|
|
IP_ADDR_EQUAL(Addr, RTE->rte_addr))) {
|
|
RTE->rte_flags |= RTE_VALID;
|
|
RTE->rte_valid = Now;
|
|
|
|
Count++;
|
|
}
|
|
|
|
RTE->rte_todg = RTE->rte_fromdg = NULL;
|
|
|
|
// To ensure that RCEs get switched to a lower-metric gateway
|
|
// if one exists, invalidate all RCEs on this RTE.
|
|
InvalidateRCEChain(RTE);
|
|
|
|
RTE = RTE->rte_next;
|
|
}
|
|
|
|
DefGWActive += Count;
|
|
UpdateDeadGWState();
|
|
return Count;
|
|
}
|
|
|
|
//* InvalidateRCE - Invalidate an RCE.
|
|
//
|
|
// Called to invalidate the RCE
|
|
//
|
|
//
|
|
// Input: RCE
|
|
//
|
|
// Returns: usecnt on the RCE.
|
|
//
|
|
uint
|
|
InvalidateRCE(RouteCacheEntry * CurrentRCE)
|
|
{
|
|
CTELockHandle RCEHandle; // Lock handle for RCE being updated.
|
|
Interface *OutIF;
|
|
RouteTableEntry *RTE;
|
|
RouteCacheEntry *PrevRCE;
|
|
uint RCE_usecnt = 0;
|
|
|
|
if (CurrentRCE != NULL) {
|
|
|
|
CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle);
|
|
|
|
RCE_usecnt = CurrentRCE->rce_usecnt;
|
|
|
|
if ((CurrentRCE->rce_flags & RCE_VALID) && !(CurrentRCE->rce_flags & RCE_LINK_DELETED)) {
|
|
ASSERT(CurrentRCE->rce_rte != NULL);
|
|
|
|
OutIF = CurrentRCE->rce_rte->rte_if;
|
|
|
|
RTE = CurrentRCE->rce_rte;
|
|
|
|
CurrentRCE->rce_rte->rte_rces -= CurrentRCE->rce_cnt;
|
|
|
|
CurrentRCE->rce_flags &= ~RCE_VALID;
|
|
CurrentRCE->rce_rte = (RouteTableEntry *) OutIF;
|
|
|
|
if ((CurrentRCE->rce_flags & RCE_CONNECTED) &&
|
|
(RCE_usecnt == 0)) {
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"InvalidateRCE %x\n", CurrentRCE));
|
|
|
|
InvalidateRCEContext(CurrentRCE);
|
|
}
|
|
PrevRCE = STRUCT_OF(RouteCacheEntry, &RTE->rte_rcelist, rce_next);
|
|
|
|
// Walk down the list until we find him.
|
|
|
|
while (PrevRCE != NULL) {
|
|
if (PrevRCE->rce_next == CurrentRCE)
|
|
break;
|
|
PrevRCE = PrevRCE->rce_next;
|
|
}
|
|
|
|
//ASSERT(PrevRCE != NULL);
|
|
if (PrevRCE != NULL) {
|
|
PrevRCE->rce_next = CurrentRCE->rce_next;
|
|
}
|
|
}
|
|
CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle);
|
|
|
|
}
|
|
return RCE_usecnt;
|
|
|
|
}
|
|
|
|
//* InvalidateRCEChain - Invalidate the RCEs on an RCE.
|
|
//
|
|
// Called to invalidate the RCE chain on an RTE. We assume the caller holds
|
|
// the route table lock.
|
|
//
|
|
// Input: RTE - RTE on which to invalidate RCEs.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
InvalidateRCEChain(RouteTableEntry * RTE)
|
|
{
|
|
CTELockHandle RCEHandle; // Lock handle for RCE being updated.
|
|
RouteCacheEntry *TempRCE, *CurrentRCE;
|
|
Interface *OutIF;
|
|
|
|
OutIF = RTE->rte_if;
|
|
|
|
// If there is an RCE chain on this RCE, invalidate the RCEs on it. We still
|
|
// hold the RouteTableLock, so RCE closes can't happen.
|
|
|
|
CurrentRCE = RTE->rte_rcelist;
|
|
RTE->rte_rcelist = NULL;
|
|
|
|
// Walk down the list, nuking each RCE.
|
|
while (CurrentRCE != NULL) {
|
|
|
|
CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle);
|
|
|
|
if ((CurrentRCE->rce_flags & RCE_VALID) && !(CurrentRCE->rce_flags & RCE_LINK_DELETED)) {
|
|
ASSERT(CurrentRCE->rce_rte == RTE);
|
|
|
|
RTE->rte_rces -= CurrentRCE->rce_cnt;
|
|
|
|
CurrentRCE->rce_flags &= ~RCE_VALID;
|
|
CurrentRCE->rce_rte = (RouteTableEntry *) OutIF;
|
|
if ((CurrentRCE->rce_flags & RCE_CONNECTED) &&
|
|
CurrentRCE->rce_usecnt == 0) {
|
|
InvalidateRCEContext(CurrentRCE);
|
|
}
|
|
} else
|
|
ASSERT(FALSE);
|
|
|
|
TempRCE = CurrentRCE->rce_next;
|
|
CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle);
|
|
CurrentRCE = TempRCE;
|
|
}
|
|
|
|
}
|
|
|
|
//* InvalidateRCELinks - Invalidate the RCEs on RTE when the link goes away
|
|
//
|
|
// Called to invalidate the RCE chain on an RTE. We assume the caller holds
|
|
// the route table lock.
|
|
//
|
|
// Input: RTE - RTE on which to invalidate RCEs.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
InvalidateRCELinks(RouteTableEntry * RTE)
|
|
{
|
|
CTELockHandle RCEHandle; // Lock handle for RCE being updated.
|
|
RouteCacheEntry *TempRCE, *CurrentRCE;
|
|
Interface *OutIF;
|
|
|
|
InvalidateRCEChain(RTE);
|
|
|
|
OutIF = RTE->rte_if;
|
|
|
|
ASSERT(OutIF->if_flags & IF_FLAGS_P2MP);
|
|
ASSERT(RTE->rte_link);
|
|
|
|
// If there is an RCE chain on this RCE, invalidate the RCEs on it. We still
|
|
// hold the RouteTableLock, so RCE closes can't happen.
|
|
|
|
CurrentRCE = RTE->rte_rcelist;
|
|
RTE->rte_rcelist = NULL;
|
|
|
|
// Walk down the list, nuking each RCE.
|
|
while (CurrentRCE != NULL) {
|
|
|
|
CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle);
|
|
|
|
// mark the RCE as link deleted so that this rce is not selected in iptransmit
|
|
CurrentRCE->rce_flags |= RCE_LINK_DELETED;
|
|
|
|
TempRCE = CurrentRCE->rce_next;
|
|
CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle);
|
|
CurrentRCE = TempRCE;
|
|
}
|
|
|
|
}
|
|
|
|
//* GetNextHopForRTE - determines the next-hop address for a route.
|
|
//
|
|
// Called when we need an actual next-hop for a route, typically so
|
|
// we can pass it to an external client. For local routes that have
|
|
// an rte_addr field set to IPADDR_LOCAL, this means figuring out
|
|
// the source NTE for the route and using its IP address.
|
|
//
|
|
// Entry: RTE - the entry whose next-hop is required
|
|
//
|
|
// Returns: IPAddr containing the next-hop
|
|
//
|
|
IPAddr
|
|
GetNextHopForRTE(RouteTableEntry* RTE)
|
|
{
|
|
if (IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL)) {
|
|
Interface *IF = RTE->rte_if;
|
|
NetTableEntry *SrcNTE = BestNTEForIF(RTE->rte_dest, IF, FALSE);
|
|
if (IF->if_nte != NULL && SrcNTE != NULL)
|
|
return SrcNTE->nte_addr;
|
|
else
|
|
return RTE->rte_dest;
|
|
}
|
|
return RTE->rte_addr;
|
|
}
|
|
|
|
//** FindValidIFForRTE - Find a valid inteface for an RTE.
|
|
//
|
|
// Called when we're going to send a packet out a route that currently marked
|
|
// as disconnected. If we have a valid callout routine we'll call it to find
|
|
// the outgoing interface index, and set up the RTE to point at that interface.
|
|
// This routine is called with the RouteTableLock held.
|
|
//
|
|
// Input: RTE - A pointer to the RTE for the route being used.
|
|
// Destination - Destination IP address we're trying to reach.
|
|
// Source - Source IP address we're sending from.
|
|
// Protocol - Protocol type of packet that caused send.
|
|
// Buffer - Pointer to first part of packet that caused send.
|
|
// Length - Length of buffer.
|
|
// HdrSrc - Src Address in header
|
|
//
|
|
// Returns: A pointer to the RTE, or NULL if that RTE couldn't be connected.
|
|
//
|
|
RouteTableEntry *
|
|
FindValidIFForRTE(RouteTableEntry * RTE, IPAddr Destination, IPAddr Source,
|
|
uchar Protocol, uchar * Buffer, uint Length, IPAddr HdrSrc)
|
|
{
|
|
uint NewIFIndex;
|
|
Interface *NewIF;
|
|
NetTableEntry *NewNTE;
|
|
|
|
if (RefPtrValid(&DODRefPtr)) {
|
|
IPMapRouteToInterfacePtr DODCallout;
|
|
|
|
// There is a callout. See if it can help us.
|
|
DODCallout = AcquireRefPtr(&DODRefPtr);
|
|
NewIFIndex = (*DODCallout) (RTE->rte_context, Destination, Source,
|
|
Protocol, Buffer, Length, HdrSrc);
|
|
ReleaseRefPtr(&DODRefPtr);
|
|
|
|
if (NewIFIndex != INVALID_IF_INDEX) {
|
|
// We got what should be a valid index. Walk our interface table list
|
|
// and see if we can find a matching interface structure.
|
|
for (NewIF = IFList; NewIF != NULL; NewIF = NewIF->if_next) {
|
|
if (NewIF->if_index == NewIFIndex) {
|
|
// Found one.
|
|
break;
|
|
}
|
|
}
|
|
if ((NewIF != NULL) && (NewIF->if_ntecount)) {
|
|
// We found a matching structure. Set the RTE interface to point
|
|
// to this, and mark as connected.
|
|
if (RTE->rte_addr != IPADDR_LOCAL) {
|
|
// See if the first hop of the route is a local address on this
|
|
// new interface. If it is, mark it as local.
|
|
for (NewNTE = NewIF->if_nte; NewNTE != NULL;
|
|
NewNTE = NewNTE->nte_ifnext) {
|
|
|
|
// Don't look at him if he's not valid.
|
|
if (!(NewNTE->nte_flags & NTE_VALID)) {
|
|
continue;
|
|
}
|
|
// See if the first hop in the RTE is equal to this IP
|
|
// address.
|
|
if (IP_ADDR_EQUAL(NewNTE->nte_addr, RTE->rte_addr)) {
|
|
// It is, so mark as local and quit looking.
|
|
RTE->rte_addr = IPADDR_LOCAL;
|
|
RTE->rte_type = IRE_TYPE_DIRECT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Set the RTE to the new interface, and mark him as valid.
|
|
RTE->rte_if = NewIF;
|
|
RTE->rte_flags |= RTE_IF_VALID;
|
|
SortRoutesInDestByRTE(RTE);
|
|
RTE->rte_mtu = NewIF->if_mtu - sizeof(IPHeader);
|
|
return RTE;
|
|
} else {
|
|
// ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
// Either the callout is NULL, or the callout couldn't map a inteface index.
|
|
return NULL;
|
|
}
|
|
|
|
//* GetRouteContext - Routine to get the route context for a specific route.
|
|
//
|
|
// Called when we need to get the route context for a path, usually when we're
|
|
// adding a route derived from an existing route. We return the route context
|
|
// for the existing route, or NULL if we can't find one.
|
|
//
|
|
// Input: Destination - Destination address of path.
|
|
// Source - Source address of path.
|
|
//
|
|
// Returns: A ROUTE_CONTEXT, or 0.
|
|
//
|
|
ROUTE_CONTEXT
|
|
GetRouteContext(IPAddr Destination, IPAddr Source)
|
|
{
|
|
CTELockHandle Handle;
|
|
RouteTableEntry *RTE;
|
|
ROUTE_CONTEXT Context;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
RTE = LookupRTE(Destination, Source, HOST_ROUTE_PRI, FALSE);
|
|
if (RTE != NULL) {
|
|
Context = RTE->rte_context;
|
|
} else
|
|
Context = 0;
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
|
|
return (Context);
|
|
}
|
|
|
|
//** LookupNextHop - Look up the next hop
|
|
//
|
|
// Called when we need to find the next hop on our way to a destination. We
|
|
// call LookupRTE to find it, and return the appropriate information.
|
|
//
|
|
// In a PnP build, the interface is referenced here.
|
|
//
|
|
// Entry: Destination - IP address we're trying to reach.
|
|
// Src - Source address of datagram being routed.
|
|
// NextHop - Pointer to IP address of next hop (returned).
|
|
// MTU - Pointer to where to return max MTU used on the
|
|
// route.
|
|
//
|
|
// Returns: Pointer to outgoing interface if we found one, NULL otherwise.
|
|
//
|
|
Interface *
|
|
LookupNextHop(IPAddr Destination, IPAddr Src, IPAddr * NextHop, uint * MTU)
|
|
{
|
|
CTELockHandle TableLock; // Lock handle for routing table.
|
|
RouteTableEntry *Route; // Pointer to route table entry for route.
|
|
Interface *IF;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &TableLock);
|
|
Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI, FALSE);
|
|
|
|
if (Route != (RouteTableEntry *) NULL) {
|
|
IF = Route->rte_if;
|
|
|
|
// If this is a direct route, send straight to the destination.
|
|
*NextHop = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? Destination :
|
|
Route->rte_addr;
|
|
|
|
// if the route is on a P2MP interface get the mtu from the link associated with the route
|
|
if (Route->rte_link)
|
|
*MTU = Route->rte_link->link_mtu;
|
|
else
|
|
*MTU = Route->rte_mtu;
|
|
|
|
LOCKED_REFERENCE_IF(IF);
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return IF;
|
|
} else { // Couldn't find a route.
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//** LookupNextHopWithBuffer - Look up the next hop, with packet information.
|
|
//
|
|
// Called when we need to find the next hop on our way to a destination and we
|
|
// have packet information that we may use for dial on demand support. We call
|
|
// LookupRTE to find it, and return the appropriate information. We may bring
|
|
// up the link if neccessary.
|
|
//
|
|
// In a PnP build, the interface is referenced here.
|
|
//
|
|
// Entry: Destination - IP address we're trying to reach.
|
|
// Src - Source address of datagram being routed.
|
|
// NextHop - Pointer to IP address of next hop (returned).
|
|
// MTU - Pointer to where to return max MTU used on the
|
|
// route.
|
|
// Protocol - Protocol type for packet that's causing this
|
|
// lookup.
|
|
// Buffer - Pointer to first part of packet causing lookup.
|
|
// Length - Length of Buffer.
|
|
// HdrSrc - source addres from header
|
|
// UnicastIf - Iface to constrain lookup to, 0 if unconstrained
|
|
//
|
|
// Returns: Pointer to outgoing interface if we found one, NULL otherwise.
|
|
//
|
|
Interface *
|
|
LookupNextHopWithBuffer(IPAddr Destination, IPAddr Src, IPAddr *NextHop,
|
|
uint * MTU, uchar Protocol, uchar *Buffer, uint Length,
|
|
RouteCacheEntry **fwdRCE, LinkEntry **Link,
|
|
IPAddr HdrSrc, uint UnicastIf)
|
|
{
|
|
CTELockHandle TableLock; // Lock handle for routing table.
|
|
RouteTableEntry *Route; // Pointer to route table entry for route.
|
|
Interface *IF;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &TableLock);
|
|
Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI, UnicastIf);
|
|
|
|
if (Route != (RouteTableEntry *) NULL) {
|
|
|
|
// If this is a direct route, send straight to the destination.
|
|
*NextHop = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? Destination :
|
|
Route->rte_addr;
|
|
|
|
// If this is an indirect route, we can use the forwarding RCE
|
|
if (fwdRCE) {
|
|
#if REM_OPT
|
|
*fwdRCE = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? NULL :
|
|
#else
|
|
*fwdRCE =
|
|
#endif
|
|
(RouteCacheEntry *) STRUCT_OF(RouteCacheEntry,
|
|
&Route->rte_arpcontext,
|
|
rce_context);
|
|
}
|
|
|
|
// See if the route we found is connected. If not, try to connect it.
|
|
if (!(Route->rte_flags & RTE_IF_VALID)) {
|
|
Route = FindValidIFForRTE(Route, Destination, Src, Protocol, Buffer,
|
|
Length, HdrSrc);
|
|
if (Route == NULL) {
|
|
// Couldn't bring it up.
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL;
|
|
} else
|
|
IF = Route->rte_if;
|
|
} else
|
|
IF = Route->rte_if;
|
|
|
|
// if the route is on a P2MP interface get the mtu from the
|
|
// link associated with the route
|
|
if (Route->rte_link)
|
|
*MTU = Route->rte_link->link_mtu;
|
|
else
|
|
*MTU = Route->rte_mtu;
|
|
|
|
if (Link) {
|
|
*Link = Route->rte_link;
|
|
if (Route->rte_link) {
|
|
CTEInterlockedIncrementLong(&Route->rte_link->link_refcount);
|
|
}
|
|
}
|
|
LOCKED_REFERENCE_IF(IF);
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return IF;
|
|
} else { // Couldn't find a route.
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//** LookupForwardingNextHop - Look up the next hop on which to forward packet on.
|
|
//
|
|
// Called when we need to find the next hop on our way to a destination and we
|
|
// have packet information that we may use for dial on demand support. We call
|
|
// LookupRTE to find it, and return the appropriate information. We may bring
|
|
// up the link if neccessary.
|
|
//
|
|
// In a PnP build, the interface is referenced here.
|
|
//
|
|
// Entry: Destination - IP address we're trying to reach.
|
|
// Src - Source address of datagram being routed.
|
|
// NextHop - Pointer to IP address of next hop (returned).
|
|
// MTU - Pointer to where to return max MTU used on the
|
|
// route.
|
|
// Protocol - Protocol type for packet that's causing this
|
|
// lookup.
|
|
// Buffer - Pointer to first part of packet causing lookup.
|
|
// Length - Length of Buffer.
|
|
// HdrSrc - source addres from header
|
|
//
|
|
// Returns: Pointer to outgoing interface if we found one, NULL otherwise.
|
|
//
|
|
Interface *
|
|
LookupForwardingNextHop(IPAddr Destination, IPAddr Src, IPAddr *NextHop,
|
|
uint * MTU, uchar Protocol, uchar *Buffer, uint Length,
|
|
RouteCacheEntry **fwdRCE, LinkEntry **Link,
|
|
IPAddr HdrSrc)
|
|
{
|
|
CTELockHandle TableLock; // Lock handle for routing table.
|
|
RouteTableEntry *Route; // Pointer to route table entry for route.
|
|
Interface *IF;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &TableLock);
|
|
Route = LookupForwardRTE(Destination, Src, TRUE);
|
|
|
|
if (Route != (RouteTableEntry *) NULL) {
|
|
|
|
// If this is a direct route, send straight to the destination.
|
|
*NextHop = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? Destination :
|
|
Route->rte_addr;
|
|
|
|
// If this is an indirect route, we can use the forwarding RCE
|
|
if (fwdRCE) {
|
|
#if REM_OPT
|
|
*fwdRCE = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? NULL :
|
|
#else
|
|
*fwdRCE =
|
|
#endif
|
|
(RouteCacheEntry *) STRUCT_OF(RouteCacheEntry,
|
|
&Route->rte_arpcontext,
|
|
rce_context);
|
|
}
|
|
|
|
// See if the route we found is connected. If not, try to connect it.
|
|
if (!(Route->rte_flags & RTE_IF_VALID)) {
|
|
Route = FindValidIFForRTE(Route, Destination, Src, Protocol, Buffer,
|
|
Length, HdrSrc);
|
|
if (Route == NULL) {
|
|
// Couldn't bring it up.
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL;
|
|
} else
|
|
IF = Route->rte_if;
|
|
} else
|
|
IF = Route->rte_if;
|
|
|
|
// if the route is on a P2MP interface get the mtu from the
|
|
// link associated with the route
|
|
if (Route->rte_link)
|
|
*MTU = Route->rte_link->link_mtu;
|
|
else
|
|
*MTU = Route->rte_mtu;
|
|
|
|
if (Link) {
|
|
*Link = Route->rte_link;
|
|
if (Route->rte_link) {
|
|
CTEInterlockedIncrementLong(&Route->rte_link->link_refcount);
|
|
}
|
|
}
|
|
LOCKED_REFERENCE_IF(IF);
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return IF;
|
|
} else { // Couldn't find a route.
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//* RTReadNext - Read the next route in the table.
|
|
//
|
|
// Called by the GetInfo code to read the next route in the table. We assume
|
|
// the context passed in is valid, and the caller has the RouteTableLock.
|
|
//
|
|
// Input: Context - Pointer to a RouteEntryContext.
|
|
// Buffer - Pointer to an IPRouteEntry structure.
|
|
//
|
|
// Returns: TRUE if more data is available to be read, FALSE is not.
|
|
//
|
|
uint
|
|
RTReadNext(void *Context, void *Buffer)
|
|
{
|
|
IPRouteEntry *IPREntry = (IPRouteEntry *) Buffer;
|
|
RouteTableEntry *CurrentRTE=NULL;
|
|
uint Now = CTESystemUpTime() / 1000L;
|
|
Interface *IF;
|
|
|
|
UINT retVal = GetNextRoute(Context, &CurrentRTE);
|
|
|
|
// Should always have the rte because we don't have empty route tables.
|
|
//
|
|
ASSERT(CurrentRTE);
|
|
|
|
// Fill in the buffer.
|
|
IF = CurrentRTE->rte_if;
|
|
|
|
IPREntry->ire_dest = CurrentRTE->rte_dest;
|
|
IPREntry->ire_index = IF->if_index;
|
|
IPREntry->ire_metric1 = CurrentRTE->rte_metric;
|
|
IPREntry->ire_metric2 = IRE_METRIC_UNUSED;
|
|
IPREntry->ire_metric3 = IRE_METRIC_UNUSED;
|
|
IPREntry->ire_metric4 = IRE_METRIC_UNUSED;
|
|
IPREntry->ire_metric5 = IRE_METRIC_UNUSED;
|
|
IPREntry->ire_nexthop = GetNextHopForRTE(CurrentRTE);
|
|
IPREntry->ire_type = (CurrentRTE->rte_flags & RTE_VALID ?
|
|
CurrentRTE->rte_type : IRE_TYPE_INVALID);
|
|
IPREntry->ire_proto = CurrentRTE->rte_proto;
|
|
IPREntry->ire_age = Now - CurrentRTE->rte_valid;
|
|
IPREntry->ire_mask = CurrentRTE->rte_mask;
|
|
IPREntry->ire_context = CurrentRTE->rte_context;
|
|
|
|
return retVal;
|
|
}
|
|
|
|
//* RTRead - Read the next route in the table.
|
|
//
|
|
// Called by the GetInfo code to read the next route in the table. We assume
|
|
// the context passed in is valid, and the caller has the RouteTableLock.
|
|
//
|
|
// Input: Context - Pointer to a RouteEntryContext.
|
|
// Buffer - Pointer to an IPRouteEntry structure.
|
|
//
|
|
// Returns:
|
|
//
|
|
|
|
//* RtRead - Read a route
|
|
//
|
|
// Returns: Status of attempt to add route.
|
|
//
|
|
uint
|
|
RTRead(void *pContext, void *pBuffer)
|
|
{
|
|
IPRouteLookupData *pRLData = (IPRouteLookupData *) pContext;
|
|
IPRouteEntry *pIPREntry = (IPRouteEntry *) pBuffer;
|
|
RouteTableEntry *pCurrentRTE;
|
|
uint Now = CTESystemUpTime() / 1000L;
|
|
Interface *pIF;
|
|
|
|
ASSERT((pContext != NULL) && (pBuffer != NULL));
|
|
pCurrentRTE = LookupRTE(pRLData->DestAdd, pRLData->SrcAdd,
|
|
HOST_ROUTE_PRI, FALSE);
|
|
|
|
if (pCurrentRTE == NULL) {
|
|
pIPREntry->ire_index = 0xffffffff;
|
|
return (uint) TDI_DEST_HOST_UNREACH;
|
|
}
|
|
// Fill in the buffer.
|
|
pIF = pCurrentRTE->rte_if;
|
|
|
|
pIPREntry->ire_dest = pCurrentRTE->rte_dest;
|
|
pIPREntry->ire_index = pIF->if_index;
|
|
pIPREntry->ire_metric1 = pCurrentRTE->rte_metric;
|
|
pIPREntry->ire_metric2 = IRE_METRIC_UNUSED;
|
|
pIPREntry->ire_metric3 = IRE_METRIC_UNUSED;
|
|
pIPREntry->ire_metric4 = IRE_METRIC_UNUSED;
|
|
pIPREntry->ire_metric5 = IRE_METRIC_UNUSED;
|
|
pIPREntry->ire_nexthop = GetNextHopForRTE(pCurrentRTE);
|
|
pIPREntry->ire_type = (pCurrentRTE->rte_flags & RTE_VALID ?
|
|
pCurrentRTE->rte_type : IRE_TYPE_INVALID);
|
|
pIPREntry->ire_proto = pCurrentRTE->rte_proto;
|
|
pIPREntry->ire_age = Now - pCurrentRTE->rte_valid;
|
|
pIPREntry->ire_mask = pCurrentRTE->rte_mask;
|
|
pIPREntry->ire_context = pCurrentRTE->rte_context;
|
|
return TDI_SUCCESS;
|
|
}
|
|
|
|
void
|
|
LookupRoute(IPRouteLookupData * pRLData, IPRouteEntry * pIpRTE)
|
|
{
|
|
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
RTRead(pRLData, pIpRTE);
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
LookupRouteInformation(void *pRouteLookupData, void *pIpRTE,
|
|
IPROUTEINFOCLASS RouteInfoClass, void *RouteInformation,
|
|
uint * RouteInfoLength)
|
|
{
|
|
return LookupRouteInformationWithBuffer(pRouteLookupData, NULL, 0, pIpRTE,
|
|
RouteInfoClass, RouteInformation,
|
|
RouteInfoLength);
|
|
}
|
|
|
|
NTSTATUS
|
|
LookupRouteInformationWithBuffer(void *pRouteLookupData, uchar * Buffer,
|
|
uint Length, void *pIpRTE,
|
|
IPROUTEINFOCLASS RouteInfoClass,
|
|
void *RouteInformation, uint * RouteInfoLength)
|
|
{
|
|
|
|
IPRouteLookupData *pRLData = (IPRouteLookupData *) pRouteLookupData;
|
|
IPRouteEntry *pIPREntry = (IPRouteEntry *) pIpRTE;
|
|
RouteTableEntry *pCurrentRTE;
|
|
uint Now = CTESystemUpTime() / 1000L;
|
|
Interface *pIF;
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
ASSERT(pRouteLookupData != NULL);
|
|
pCurrentRTE = LookupRTE(pRLData->DestAdd, pRLData->SrcAdd, HOST_ROUTE_PRI, FALSE);
|
|
|
|
if (pCurrentRTE == NULL) {
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
// see if the RTE is for a demand-dial route,
|
|
if (!(pCurrentRTE->rte_flags & RTE_IF_VALID)) {
|
|
pCurrentRTE = FindValidIFForRTE(pCurrentRTE, pRLData->DestAdd,
|
|
pRLData->SrcAdd, pRLData->Info[0],
|
|
Buffer, Length, pRLData->SrcAdd);
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
if (pCurrentRTE == NULL) {
|
|
// Couldn't bring it up.
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
return STATUS_PENDING;
|
|
}
|
|
// Fill in the buffer.
|
|
pIF = pCurrentRTE->rte_if;
|
|
|
|
if (pIPREntry) {
|
|
pIPREntry->ire_dest = pCurrentRTE->rte_dest;
|
|
pIPREntry->ire_index = pIF->if_index;
|
|
pIPREntry->ire_metric1 = pCurrentRTE->rte_metric;
|
|
pIPREntry->ire_metric2 = IRE_METRIC_UNUSED;
|
|
pIPREntry->ire_metric3 = IRE_METRIC_UNUSED;
|
|
pIPREntry->ire_metric4 = IRE_METRIC_UNUSED;
|
|
pIPREntry->ire_metric5 = IRE_METRIC_UNUSED;
|
|
pIPREntry->ire_nexthop = GetNextHopForRTE(pCurrentRTE);
|
|
pIPREntry->ire_type = (pCurrentRTE->rte_flags & RTE_VALID ?
|
|
pCurrentRTE->rte_type : IRE_TYPE_INVALID);
|
|
pIPREntry->ire_proto = pCurrentRTE->rte_proto;
|
|
pIPREntry->ire_age = Now - pCurrentRTE->rte_valid;
|
|
pIPREntry->ire_mask = pCurrentRTE->rte_mask;
|
|
pIPREntry->ire_context = pCurrentRTE->rte_context;
|
|
}
|
|
switch (RouteInfoClass) {
|
|
case IPRouteOutgoingFirewallContext:
|
|
*(PULONG) RouteInformation = pIF->if_index;
|
|
*(PULONG) RouteInfoLength = sizeof(PVOID);
|
|
break;
|
|
|
|
case IPRouteOutgoingFilterContext:
|
|
*(PVOID *) RouteInformation = NULL;
|
|
*(PULONG) RouteInfoLength = sizeof(PVOID);
|
|
break;
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//* DeleteRTE - Delete an RTE.
|
|
//
|
|
// Called when we need to delete an RTE. We assume the caller has the
|
|
// RouteTableLock. We'll splice out the RTE, invalidate his RCEs, and
|
|
// free the memory.
|
|
//
|
|
// Input: PrevRTE - RTE in 'front' of one being deleted.
|
|
// RTE - RTE to be deleted.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
DeleteRTE(RouteTableEntry * PrevRTE, RouteTableEntry * RTE)
|
|
{
|
|
UNREFERENCED_PARAMETER(PrevRTE);
|
|
|
|
IPSInfo.ipsi_numroutes--;
|
|
|
|
if (RTE->rte_mask == DEFAULT_MASK) {
|
|
// We're deleting a default route.
|
|
DefGWConfigured--;
|
|
if (RTE->rte_flags & RTE_VALID)
|
|
DefGWActive--;
|
|
UpdateDeadGWState();
|
|
if (DefGWActive == 0)
|
|
ValidateDefaultGWs(NULL_IP_ADDR);
|
|
|
|
}
|
|
|
|
if (RTE->rte_todg) {
|
|
RTE->rte_todg->rte_fromdg = NULL;
|
|
}
|
|
if (RTE->rte_fromdg) {
|
|
RTE->rte_fromdg->rte_todg = NULL;
|
|
}
|
|
|
|
{
|
|
RouteTableEntry *tmpRTE = NULL;
|
|
tmpRTE = GetDefaultGWs(&tmpRTE);
|
|
|
|
while (tmpRTE) {
|
|
if (tmpRTE->rte_todg == RTE) {
|
|
tmpRTE->rte_todg = NULL;
|
|
}
|
|
tmpRTE = tmpRTE->rte_next;
|
|
}
|
|
}
|
|
|
|
InvalidateRCEChain(RTE);
|
|
|
|
// Make sure RTE's IF is valid
|
|
ASSERT(RTE->rte_if != NULL);
|
|
|
|
// Invalidate the fwding rce
|
|
|
|
if (RTE->rte_if != (Interface *) & DummyInterface) {
|
|
(*(RTE->rte_if->if_invalidate)) (RTE->rte_if->if_lcontext,
|
|
(RouteCacheEntry *) STRUCT_OF(RouteCacheEntry,
|
|
&RTE->rte_arpcontext,
|
|
rce_context));
|
|
}
|
|
|
|
// Free the old route.
|
|
FreeRoute(RTE);
|
|
}
|
|
|
|
//* DeleteRTEOnIF - Delete all address-dependent RTEs on a particular IF.
|
|
//
|
|
// A function called by RTWalk when we want to delete all RTEs on a particular
|
|
// inteface, except those that are present for the lifetime of the interface.
|
|
// We just check the I/F of each RTE, and if it matches we return FALSE.
|
|
//
|
|
// Input: RTE - RTE to check.
|
|
// Context - Interface on which we're deleting.
|
|
//
|
|
// Returns: FALSE if we want to delete it, TRUE otherwise.
|
|
//
|
|
uint
|
|
DeleteRTEOnIF(RouteTableEntry * RTE, void *Context, void *Context1)
|
|
{
|
|
Interface *IF = (Interface *) Context;
|
|
|
|
UNREFERENCED_PARAMETER(Context1);
|
|
|
|
if (RTE->rte_if == IF && !IP_ADDR_EQUAL(RTE->rte_dest, IF->if_bcast))
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
//* DeleteAllRTEOnIF - Delete all RTEs on a particular IF.
|
|
//
|
|
// A function called by RTWalk when we want to delete all RTEs on a particular
|
|
// inteface. We just check the I/F of each RTE, and if it matches we return
|
|
// FALSE.
|
|
//
|
|
// Input: RTE - RTE to check.
|
|
// Context - Interface on which we're deleting.
|
|
//
|
|
// Returns: FALSE if we want to delete it, TRUE otherwise.
|
|
//
|
|
uint
|
|
DeleteAllRTEOnIF(RouteTableEntry * RTE, void *Context, void *Context1)
|
|
{
|
|
Interface *IF = (Interface *) Context;
|
|
|
|
UNREFERENCED_PARAMETER(Context1);
|
|
|
|
if (RTE->rte_if == IF)
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
//* ConvertRTEType -Change RTE type from DIRECT INDIRECT to DIRECT.
|
|
//
|
|
// A function called by RTWalk when an address is added to chnage
|
|
// P2P/P2MP plumbed route to DIRECT type.
|
|
//
|
|
// Input: RTE - RTE to check.
|
|
// Context - Interface on which we're invalidating.
|
|
//
|
|
// Returns: TRUE.
|
|
//
|
|
uint
|
|
ConvertRTEType(RouteTableEntry * RTE, void *Context, void *Context1)
|
|
{
|
|
NetTableEntry *NTE = (NetTableEntry *) Context;
|
|
|
|
UNREFERENCED_PARAMETER(Context1);
|
|
|
|
if ((RTE->rte_addr == NTE->nte_addr) &&
|
|
(RTE->rte_if == NTE->nte_if) &&
|
|
(RTE->rte_type == IRE_TYPE_DIRECT)) {
|
|
RTE->rte_addr = IPADDR_LOCAL;
|
|
RTE->rte_type = IRE_TYPE_INDIRECT;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
//* InvalidateRCEOnIF - Invalidate all RCEs on a particular IF.
|
|
//
|
|
// A function called by RTWalk when we want to invalidate all RCEs on a
|
|
// particular inteface. We just check the I/F of each RTE, and if it
|
|
// matches we call InvalidateRCEChain to invalidate the RCEs.
|
|
//
|
|
// Input: RTE - RTE to check.
|
|
// Context - Interface on which we're invalidating.
|
|
//
|
|
// Returns: TRUE.
|
|
//
|
|
uint
|
|
InvalidateRCEOnIF(RouteTableEntry * RTE, void *Context, void *Context1)
|
|
{
|
|
Interface *IF = (Interface *) Context;
|
|
|
|
UNREFERENCED_PARAMETER(Context1);
|
|
|
|
if (RTE->rte_if == IF)
|
|
InvalidateRCEChain(RTE);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
//* SetMTUOnIF - Set the MTU on an interface.
|
|
//
|
|
// Called when we need to set the MTU on an interface.
|
|
//
|
|
// Input: RTE - RTE to check.
|
|
// Context - Pointer to a context.
|
|
// Context1 - Pointer to the new MTU.
|
|
//
|
|
// Returns: TRUE.
|
|
//
|
|
uint
|
|
SetMTUOnIF(RouteTableEntry * RTE, void *Context, void *Context1)
|
|
{
|
|
uint NewMTU = *(uint *) Context1;
|
|
Interface *IF = (Interface *) Context;
|
|
|
|
if (RTE->rte_if == IF)
|
|
RTE->rte_mtu = NewMTU;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* SetMTUToAddr - Set the MTU to a specific address.
|
|
//
|
|
// Called when we need to set the MTU to a specific address. We set the MTU
|
|
// for all routes that use the specified address as a first hop to the new
|
|
// MTU.
|
|
//
|
|
// Input: RTE - RTE to check.
|
|
// Context - Pointer to a context.
|
|
// Context1 - Pointer to the new MTU.
|
|
//
|
|
// Returns: TRUE.
|
|
//
|
|
uint
|
|
SetMTUToAddr(RouteTableEntry * RTE, void *Context, void *Context1)
|
|
{
|
|
uint NewMTU = *(uint *) Context1;
|
|
IPAddr Addr = *(IPAddr *) Context;
|
|
|
|
if (IP_ADDR_EQUAL(RTE->rte_addr, Addr))
|
|
RTE->rte_mtu = NewMTU;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//** FreeRtChangeList - Frees a route-change notification list.
|
|
//
|
|
// Called to clean up a list of route-change notifications in the failure path
|
|
// of 'RTWalk' and 'IPRouteTimeout'.
|
|
//
|
|
// Entry: RtChangeList - The list to be freed.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
FreeRtChangeList(RtChangeList* CurrentRtChangeList)
|
|
{
|
|
RtChangeList *TmpRtChangeList;
|
|
while (CurrentRtChangeList) {
|
|
TmpRtChangeList = CurrentRtChangeList->rt_next;
|
|
CTEFreeMem(CurrentRtChangeList);
|
|
CurrentRtChangeList = TmpRtChangeList;
|
|
}
|
|
}
|
|
|
|
//* RTWalk - Routine to walk the route table.
|
|
//
|
|
// This routine walks the route table, calling the specified function
|
|
// for each entry. If the called function returns FALSE, the RTE is
|
|
// deleted.
|
|
//
|
|
// Input: CallFunc - Function to call for each entry.
|
|
// Context - Context value to pass to each call.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
RTWalk(uint(*CallFunc) (struct RouteTableEntry *, void *, void *),
|
|
void *Context, void *Context1)
|
|
{
|
|
CTELockHandle Handle;
|
|
RouteTableEntry *RTE, *PrevRTE;
|
|
RouteTableEntry *pOldBestRTE, *pNewBestRTE;
|
|
UINT IsDataLeft, IsValid;
|
|
UCHAR IteratorContext[CONTEXT_SIZE];
|
|
RtChangeList *CurrentRtChangeList = NULL;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
// Zero the context the first time it is used
|
|
RtlZeroMemory(IteratorContext, CONTEXT_SIZE);
|
|
|
|
// Do we have any routes in the table ?
|
|
IsDataLeft = RTValidateContext(IteratorContext, &IsValid);
|
|
|
|
if (IsDataLeft) {
|
|
// Get the first route in the table
|
|
IsDataLeft = GetNextRoute(IteratorContext, &RTE);
|
|
|
|
while (IsDataLeft) {
|
|
// Keep copy of current route and advance to next
|
|
PrevRTE = RTE;
|
|
|
|
// Read next route, before operating on current
|
|
IsDataLeft = GetNextRoute(IteratorContext, &RTE);
|
|
|
|
// Work on current route (already got next one)
|
|
if (!(*CallFunc) (PrevRTE, Context, Context1)) {
|
|
IPRouteNotifyOutput RNO = {0};
|
|
RtChangeList *NewRtChange;
|
|
|
|
// Retrieve information about the route for change-notification
|
|
// before proceeding with deletion.
|
|
|
|
RNO.irno_dest = PrevRTE->rte_dest;
|
|
RNO.irno_mask = PrevRTE->rte_mask;
|
|
RNO.irno_nexthop = GetNextHopForRTE(PrevRTE);
|
|
RNO.irno_proto = PrevRTE->rte_proto;
|
|
RNO.irno_ifindex = PrevRTE->rte_if->if_index;
|
|
RNO.irno_metric = PrevRTE->rte_metric;
|
|
RNO.irno_flags = IRNO_FLAG_DELETE;
|
|
|
|
// Delete the route and perform cleanup.
|
|
|
|
DelRoute(PrevRTE->rte_dest, PrevRTE->rte_mask,
|
|
PrevRTE->rte_addr, PrevRTE->rte_if, MATCH_FULL,
|
|
&PrevRTE, &pOldBestRTE, &pNewBestRTE);
|
|
|
|
CleanupP2MP_RTE(PrevRTE);
|
|
CleanupRTE(PrevRTE);
|
|
|
|
// Allocate, initialize and queue a change-notification entry
|
|
// for the deleted route.
|
|
|
|
NewRtChange = CTEAllocMemNBoot(sizeof(RtChangeList), '9iCT');
|
|
if (NewRtChange != NULL) {
|
|
NewRtChange->rt_next = CurrentRtChangeList;
|
|
NewRtChange->rt_info = RNO;
|
|
CurrentRtChangeList = NewRtChange;
|
|
}
|
|
|
|
#if FFP_SUPPORT
|
|
FFPFlushRequired = TRUE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Work on last route [it was not processed in the loop]
|
|
PrevRTE = RTE;
|
|
|
|
if (!(*CallFunc) (PrevRTE, Context, Context1)) {
|
|
|
|
IPRouteNotifyOutput RNO = {0};
|
|
RtChangeList *NewRtChange;
|
|
|
|
// Retrieve information about the route for change-notification
|
|
// before proceeding with deletion.
|
|
|
|
RNO.irno_dest = PrevRTE->rte_dest;
|
|
RNO.irno_mask = PrevRTE->rte_mask;
|
|
RNO.irno_nexthop = GetNextHopForRTE(PrevRTE);
|
|
RNO.irno_proto = PrevRTE->rte_proto;
|
|
RNO.irno_ifindex = PrevRTE->rte_if->if_index;
|
|
RNO.irno_metric = PrevRTE->rte_metric;
|
|
RNO.irno_flags = IRNO_FLAG_DELETE;
|
|
|
|
// Delete the route and perform cleanup.
|
|
|
|
DelRoute(PrevRTE->rte_dest, PrevRTE->rte_mask, PrevRTE->rte_addr,
|
|
PrevRTE->rte_if, MATCH_FULL, &PrevRTE, &pOldBestRTE,
|
|
&pNewBestRTE);
|
|
|
|
CleanupP2MP_RTE(PrevRTE);
|
|
CleanupRTE(PrevRTE);
|
|
|
|
// Allocate, initialize and queue a change-notification entry
|
|
// for the deleted route.
|
|
|
|
NewRtChange = CTEAllocMemNBoot(sizeof(RtChangeList), '0iCT');
|
|
if (NewRtChange != NULL) {
|
|
NewRtChange->rt_next = CurrentRtChangeList;
|
|
NewRtChange->rt_info = RNO;
|
|
CurrentRtChangeList = NewRtChange;
|
|
}
|
|
|
|
#if FFP_SUPPORT
|
|
FFPFlushRequired = TRUE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
|
|
// Call RtChangeNotify for each of the entries in the change-notification
|
|
// list that we've built up so far. In the process, free each entry.
|
|
|
|
if (CurrentRtChangeList) {
|
|
RtChangeList *TmpRtChangeList;
|
|
|
|
do {
|
|
TmpRtChangeList = CurrentRtChangeList->rt_next;
|
|
RtChangeNotify(&CurrentRtChangeList->rt_info);
|
|
CTEFreeMem(CurrentRtChangeList);
|
|
CurrentRtChangeList = TmpRtChangeList;
|
|
} while(CurrentRtChangeList);
|
|
}
|
|
}
|
|
|
|
uint
|
|
AttachRCEToNewRTE(RouteTableEntry *NewRTE, RouteCacheEntry *RCE,
|
|
RouteTableEntry *OldRTE)
|
|
{
|
|
CTELockHandle RCEHandle;
|
|
RouteCacheEntry *tempRCE, *CurrentRCE;
|
|
NetTableEntry *NTE;
|
|
uint Status = 1;
|
|
uint RCE_usecnt;
|
|
|
|
if (RCE == NULL) {
|
|
CurrentRCE = OldRTE->rte_rcelist;
|
|
|
|
} else {
|
|
CurrentRCE = RCE;
|
|
}
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AttachRCETonewRTE %x %x %x\n", NewRTE, RCE, OldRTE));
|
|
|
|
// OldRTE = RCE->rce_rte;
|
|
|
|
//associate all the RCEs with this RTE
|
|
|
|
while (CurrentRCE != NULL) {
|
|
|
|
RCE_usecnt = InvalidateRCE(CurrentRCE);
|
|
|
|
CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle);
|
|
|
|
tempRCE = CurrentRCE->rce_next;
|
|
|
|
// if no one is using this go ahead and
|
|
// mark this as valid
|
|
|
|
if (RCE_usecnt == 0) {
|
|
|
|
//Make sure that the src address for RCE is valid
|
|
//for this RTE
|
|
|
|
NTE = NewRTE->rte_if->if_nte;
|
|
|
|
while (NTE) {
|
|
|
|
if ((NTE->nte_flags & NTE_VALID) &&
|
|
IP_ADDR_EQUAL(CurrentRCE->rce_src, NTE->nte_addr))
|
|
break;
|
|
NTE = NTE->nte_ifnext;
|
|
}
|
|
|
|
if (NTE != NULL) {
|
|
|
|
if (CurrentRCE->rce_flags & RCE_CONNECTED) {
|
|
InvalidateRCEContext(CurrentRCE);
|
|
} else {
|
|
ASSERT(!(CurrentRCE->rce_flags & RCE_REFERENCED));
|
|
}
|
|
|
|
// Link the RCE on the RTE, and set up the back pointer.
|
|
CurrentRCE->rce_rte = NewRTE;
|
|
CurrentRCE->rce_flags |= RCE_VALID;
|
|
CurrentRCE->rce_next = NewRTE->rte_rcelist;
|
|
NewRTE->rte_rcelist = CurrentRCE;
|
|
|
|
NewRTE->rte_rces += CurrentRCE->rce_cnt;
|
|
|
|
if ((NewRTE->rte_flags & RTE_IF_VALID)) {
|
|
|
|
CurrentRCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED);
|
|
LOCKED_REFERENCE_IF(NewRTE->rte_if);
|
|
} else {
|
|
|
|
ASSERT(FALSE);
|
|
CurrentRCE->rce_flags &= ~RCE_CONNECTED;
|
|
Status = FALSE;
|
|
}
|
|
|
|
} //if NTE!=NULL
|
|
|
|
} else {
|
|
|
|
// In use. Mark it as in dead gw transit mmode
|
|
// so that attachtorte will do the right thing
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AttachRCETonewRTE RCE busy\n"));
|
|
// CurrentRCE->rce_rte = NewRTE;
|
|
|
|
CurrentRCE->rce_flags |= RCE_DEADGW;
|
|
|
|
} //in use
|
|
|
|
CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle);
|
|
|
|
//if there is only one RCE to be switched, break.
|
|
|
|
if (RCE)
|
|
break;
|
|
|
|
CurrentRCE = tempRCE;
|
|
|
|
} //while
|
|
|
|
return (Status);
|
|
}
|
|
|
|
//** AttachRCEToRTE - Attach an RCE to an RTE.
|
|
//
|
|
// This procedure takes an RCE, finds the appropriate RTE, and attaches it.
|
|
// We check to make sure that the source address is still valid.
|
|
//
|
|
// Entry: RCE - RCE to be attached.
|
|
// Protocol - Protocol type for packet causing this call.
|
|
// Buffer - Pointer to buffer for packet causing this
|
|
// call.
|
|
// Length - Length of buffer.
|
|
//
|
|
// Returns: TRUE if we attach it, false if we don't.
|
|
//
|
|
uint
|
|
AttachRCEToRTE(RouteCacheEntry *RCE, uchar Protocol, uchar *Buffer, uint Length)
|
|
{
|
|
CTELockHandle TableHandle, RCEHandle;
|
|
RouteTableEntry *RTE;
|
|
NetTableEntry *NTE;
|
|
uint Status;
|
|
NetTableEntry *NetTableList;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &TableHandle);
|
|
|
|
NetTableList = NewNetTableList[NET_TABLE_HASH(RCE->rce_src)];
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
|
|
if ((NTE->nte_flags & NTE_VALID) &&
|
|
IP_ADDR_EQUAL(RCE->rce_src, NTE->nte_addr))
|
|
break;
|
|
|
|
if (NTE == NULL) {
|
|
// Didn't find a match.
|
|
CTEFreeLock(&RouteTableLock.Lock, TableHandle);
|
|
return FALSE;
|
|
}
|
|
if ((RCE->rce_flags == RCE_VALID) && (RCE->rce_rte->rte_flags != RTE_IF_VALID)) {
|
|
RTE = RCE->rce_rte;
|
|
} else {
|
|
RTE = LookupRTE(RCE->rce_dest, RCE->rce_src, HOST_ROUTE_PRI, FALSE);
|
|
}
|
|
|
|
if (RTE == NULL) {
|
|
// No route! Fail the call.
|
|
CTEFreeLock(&RouteTableLock.Lock, TableHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
// Check if this RCE is in transition (usecnt did not permit
|
|
// to swicthover earlier)
|
|
|
|
if ((RCE->rce_flags & RCE_DEADGW) && (RCE->rce_rte != RTE)) {
|
|
|
|
RouteTableEntry *tmpRTE = NULL;
|
|
|
|
|
|
// Scan through DefaultGWs checking
|
|
// for a GW that is in the process of
|
|
// taking over from the current one.
|
|
|
|
|
|
if (RTE->rte_todg) {
|
|
tmpRTE = GetDefaultGWs(&tmpRTE);
|
|
|
|
while (tmpRTE) {
|
|
if (tmpRTE == RTE->rte_todg) {
|
|
break;
|
|
}
|
|
tmpRTE = tmpRTE->rte_next;
|
|
}
|
|
|
|
}
|
|
if (tmpRTE) {
|
|
|
|
// Remove references to GW
|
|
// in transition and the current one
|
|
|
|
ASSERT(tmpRTE->rte_fromdg == RTE);
|
|
tmpRTE->rte_fromdg = NULL;
|
|
RTE->rte_todg = NULL;
|
|
}
|
|
|
|
Rcefailures++;
|
|
}
|
|
|
|
Status = TRUE;
|
|
|
|
// Yep, we found one. Get the lock on the RCE, and make sure he's
|
|
// not pointing at an RTE already. We also need to make sure that the usecnt
|
|
// is 0, so that we can invalidate the RCE at the low level. If we set valid
|
|
// to TRUE without doing this we may get into a wierd situation where we
|
|
// link the RCE onto an RTE but the lower layer information is wrong, so we
|
|
// send to IP address X at mac address Y. So to be safe we don't set valid
|
|
// to TRUE until both usecnt is 0 and valid is FALSE. We'll keep coming
|
|
// through this routine on every send until that happens.
|
|
|
|
CTEGetLock(&RCE->rce_lock, &RCEHandle);
|
|
if (RCE->rce_usecnt == 0) {
|
|
// Nobody is using him, so we can link him up.
|
|
if (!(RCE->rce_flags & RCE_VALID)) {
|
|
|
|
// He's not valid. Invalidate the lower layer info, just in
|
|
// case. Make sure he's connected before we try to do this. If
|
|
// he's not marked as connected, don't bother to try and invalidate
|
|
// him as there is no interface.
|
|
|
|
if (RCE->rce_flags & RCE_CONNECTED) {
|
|
|
|
// invalidating this IF can fail in PNP world. An invalid RCE can not be found on on RTE list
|
|
// to be invalidated if Interface decides to take off!
|
|
// So, check the sanity of the interface
|
|
|
|
InvalidateRCEContext(RCE);
|
|
|
|
} else {
|
|
ASSERT(!(RCE->rce_flags & RCE_REFERENCED));
|
|
}
|
|
|
|
// Link the RCE on the RTE, and set up the back pointer.
|
|
RCE->rce_rte = RTE;
|
|
RCE->rce_flags |= RCE_VALID;
|
|
RCE->rce_next = RTE->rte_rcelist;
|
|
RTE->rte_rcelist = RCE;
|
|
RTE->rte_rces += RCE->rce_cnt;
|
|
RCE->rce_flags &= ~RCE_DEADGW;
|
|
|
|
// Make sure the RTE is connected. If not, try to connect him.
|
|
if (!(RTE->rte_flags & RTE_IF_VALID)) {
|
|
// Not connected. Try to connect him.
|
|
RTE = FindValidIFForRTE(RTE, RCE->rce_dest, RCE->rce_src,
|
|
Protocol, Buffer, Length, RCE->rce_src);
|
|
if (RTE != NULL) {
|
|
// Got one, so mark as connected.
|
|
ASSERT(!(RCE->rce_flags & RCE_REFERENCED));
|
|
RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED);
|
|
LOCKED_REFERENCE_IF(RTE->rte_if);
|
|
} else {
|
|
|
|
// Couldn't get a valid i/f. Mark the RCE as not connected,
|
|
// and set up to fail this call.
|
|
RCE->rce_flags &= ~RCE_CONNECTED;
|
|
Status = FALSE;
|
|
}
|
|
} else {
|
|
// The RTE is connected, mark the RCE as connected.
|
|
ASSERT(!(RCE->rce_flags & RCE_REFERENCED));
|
|
RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED);
|
|
LOCKED_REFERENCE_IF(RTE->rte_if);
|
|
}
|
|
} else {
|
|
|
|
// The RCE is valid. See if it's connected.
|
|
if (!(RCE->rce_flags & RCE_CONNECTED)) {
|
|
|
|
// Not connected, try to get a valid i/f.
|
|
if (!(RTE->rte_flags & RTE_IF_VALID)) {
|
|
RTE = FindValidIFForRTE(RTE, RCE->rce_dest, RCE->rce_src,
|
|
Protocol, Buffer, Length, RCE->rce_src);
|
|
if (RTE != NULL) {
|
|
RCE->rce_flags |= RCE_CONNECTED;
|
|
ASSERT(!(RCE->rce_flags & RCE_REFERENCED));
|
|
ASSERT(RTE == RCE->rce_rte);
|
|
RCE->rce_flags |= RCE_REFERENCED;
|
|
LOCKED_REFERENCE_IF(RTE->rte_if);
|
|
} else {
|
|
|
|
// Couldn't connect, so fail.
|
|
Status = FALSE;
|
|
}
|
|
} else { // Already connected, just mark as valid.
|
|
|
|
RCE->rce_flags |= RCE_CONNECTED;
|
|
if (!(RCE->rce_flags & RCE_REFERENCED)) {
|
|
RCE->rce_flags |= RCE_REFERENCED;
|
|
LOCKED_REFERENCE_IF(RTE->rte_if);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Free the locks and we're done.
|
|
CTEFreeLock(&RCE->rce_lock, RCEHandle);
|
|
CTEFreeLock(&RouteTableLock.Lock, TableHandle);
|
|
return Status;
|
|
|
|
}
|
|
|
|
//** IPGetPInfo - Get information..
|
|
//
|
|
// Called by an upper layer to get information about a path. We return the
|
|
// MTU of the path and the maximum link speed to be expected on the path.
|
|
//
|
|
// Input: Dest - Destination address.
|
|
// Src - Src address.
|
|
// NewMTU - Where to store path MTU (may be NULL).
|
|
// MaxPathSpeed - Where to store maximum path speed (may be NULL).
|
|
// RCE - RCE to be used to find the route
|
|
//
|
|
// Returns: Status of attempt to get new MTU.
|
|
//
|
|
IP_STATUS
|
|
IPGetPInfo(IPAddr Dest, IPAddr Src, uint * NewMTU, uint *MaxPathSpeed,
|
|
RouteCacheEntry *RCE)
|
|
{
|
|
CTELockHandle Handle;
|
|
RouteTableEntry *RTE = NULL;
|
|
IP_STATUS Status;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
if (RCE) {
|
|
CTEGetLockAtDPC(&RCE->rce_lock);
|
|
if (RCE->rce_flags == RCE_ALL_VALID) {
|
|
RTE = RCE->rce_rte;
|
|
}
|
|
CTEFreeLockFromDPC(&RCE->rce_lock);
|
|
}
|
|
|
|
if (!RTE) {
|
|
RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI, FALSE);
|
|
}
|
|
if (RTE != NULL) {
|
|
if (NewMTU != NULL) {
|
|
// if the route is on a P2MP interface get the mtu from the link associated with the route
|
|
if (RTE->rte_link)
|
|
*NewMTU = RTE->rte_link->link_mtu;
|
|
else
|
|
*NewMTU = RTE->rte_mtu;
|
|
}
|
|
if (MaxPathSpeed != NULL)
|
|
*MaxPathSpeed = RTE->rte_if->if_speed;
|
|
Status = IP_SUCCESS;
|
|
} else
|
|
Status = IP_DEST_HOST_UNREACHABLE;
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return Status;
|
|
|
|
}
|
|
|
|
//** IPCheckRoute - Check that a route is valid.
|
|
//
|
|
// Called by an upper layer when it believes a route might be invalid.
|
|
// We'll check if we can. If the upper layer is getting there through a
|
|
// route derived via ICMP (presumably a redirect) we'll check to see
|
|
// if it's been learned within the last minute. If it has, it's assumed
|
|
// to still be valid. Otherwise, we'll mark it as down and try to find
|
|
// another route there. If we can, we'll delete the old route. Otherwise
|
|
// we'll leave it. If the route is through a default gateway we'll switch
|
|
// to another one if we can. Otherwise, we'll just leave - we don't mess
|
|
// with manually configured routes.
|
|
//
|
|
// Input: Dest - Destination to be reached.
|
|
// Src - Src we're sending from.
|
|
// RCE - route-cache-entry to be updated.
|
|
// OptInfo - options to use if recreating the RCE
|
|
// CheckRouteFlag - modifies this routine's behavior
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
IPCheckRoute(IPAddr Dest, IPAddr Src, RouteCacheEntry * RCE, IPOptInfo *OptInfo,
|
|
uint CheckRouteFlag)
|
|
{
|
|
RouteTableEntry *RTE;
|
|
RouteTableEntry *NewRTE;
|
|
CTELockHandle Handle;
|
|
uint Now = CTESystemUpTime() / 1000L;
|
|
|
|
if (DeadGWDetect) {
|
|
uint UnicastIf;
|
|
|
|
// We are doing dead G/W detection. Get the lock, and try and
|
|
// find the route.
|
|
|
|
// Decide whether to do a strong or weak host lookup.
|
|
UnicastIf = GetIfConstraint(Dest, Src, OptInfo, FALSE);
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI, UnicastIf);
|
|
if (RTE != NULL && ((Now - RTE->rte_valid) > MIN_RT_VALID)) {
|
|
|
|
// Found a route, and it's older than the minimum valid time. If it
|
|
// goes through a G/W, and is a route we learned via ICMP or is a
|
|
// default route, do something with it.
|
|
if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL)) {
|
|
// It is through a G/W.
|
|
|
|
if (RTE->rte_proto == IRE_PROTO_ICMP) {
|
|
|
|
// Came from ICMP. Mark as invalid, and then make sure
|
|
// we have another route there.
|
|
RTE->rte_flags &= ~RTE_VALID;
|
|
NewRTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI, UnicastIf);
|
|
|
|
if (NewRTE == NULL) {
|
|
// Can't get there any other way so leave this
|
|
// one alone.
|
|
RTE->rte_flags |= RTE_VALID;
|
|
|
|
// Re validate all the other gateways
|
|
InvalidateRCEChain(RTE);
|
|
ValidateDefaultGWs(NULL_IP_ADDR);
|
|
}
|
|
// The discovered route under the
|
|
// NTE is not cleaned up.
|
|
// Since deleting the route itself does not serve any purpose and
|
|
// the route will time out eventually, let us leave this
|
|
// as invalid.
|
|
|
|
} else {
|
|
if (RTE->rte_mask == DEFAULT_MASK) {
|
|
|
|
// This is a default gateway. If we have more than one
|
|
// configured move to the next one.
|
|
|
|
if (DefGWConfigured > 1) {
|
|
// Have more than one. Try the next one. First
|
|
// invalidate any RCEs on this G/W.
|
|
|
|
if (DefGWActive == 1) {
|
|
// No more active. Revalidate all of them,
|
|
// and try again.
|
|
ValidateDefaultGWs(NULL_IP_ADDR);
|
|
// ASSERT(DefGWActive == DefGWConfigured);
|
|
} else {
|
|
|
|
//Make sure that we do not switch all the
|
|
//connections just because of a spurious
|
|
//dead gate way event.
|
|
//switch only when % of number of connections are
|
|
// failed over to the other gateway.
|
|
|
|
// if we have already found the next default gateway
|
|
// check if it is time to switch all the connections
|
|
// to it.
|
|
|
|
if (RTE->rte_todg) {
|
|
|
|
#if DBG
|
|
{
|
|
RouteTableEntry *tmpRTE = NULL;
|
|
tmpRTE = GetDefaultGWs(&tmpRTE);
|
|
|
|
while (tmpRTE) {
|
|
if (tmpRTE == RTE->rte_todg) {
|
|
break;
|
|
}
|
|
tmpRTE = tmpRTE->rte_next;
|
|
}
|
|
if (tmpRTE == NULL) {
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"to todg %lx\n", RTE));
|
|
// If the alternate gateway now has 25%
|
|
// as many as the active gateway
|
|
// and the caller has not requested
|
|
// a switch for this RCE only,
|
|
// invalidate the active gateway and
|
|
// select the alternate as the new default.
|
|
// Try different GW if there is only one RCE.
|
|
// This will help udp sessions.
|
|
//
|
|
|
|
if ((RTE->rte_rcelist == RCE &&
|
|
RCE->rce_next == NULL) ||
|
|
(RTE->rte_todg->rte_rces >=
|
|
(RTE->rte_rces >> 2) &&
|
|
!(CheckRouteFlag & CHECK_RCE_ONLY))) {
|
|
|
|
//Switch every one.
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," Switching every one %x to %x\n", RTE->rte_todg, RTE));
|
|
--DefGWActive;
|
|
RTE->rte_flags &= ~RTE_VALID;
|
|
UpdateDeadGWState();
|
|
|
|
RTE->rte_todg->rte_fromdg = NULL;
|
|
RTE->rte_todg = NULL;
|
|
|
|
if (RTE->rte_fromdg) {
|
|
RTE->rte_fromdg->rte_todg = NULL;
|
|
}
|
|
RTE->rte_fromdg = NULL;
|
|
InvalidateRCEChain(RTE);
|
|
//ASSERT(RTE->rte_rces == 0);
|
|
|
|
} else {
|
|
|
|
//Switch this particular connection to the new one.
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," attaching RCE %x to newrte %x\n", RCE, RTE->rte_todg));
|
|
AttachRCEToNewRTE(RTE->rte_todg, RCE, RTE);
|
|
}
|
|
|
|
} else if (RTE->rte_fromdg) {
|
|
|
|
// find if there are any other gateways other than
|
|
// fromdg and switch to that.
|
|
// Note that if we have more than 3 default gateways
|
|
// configured, this algorithm does not do a god job
|
|
|
|
RouteTableEntry *OldRTE = RTE;
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"GW %x goofed. RTEfromdg %x\n",RTE,RTE->rte_fromdg));
|
|
|
|
--DefGWActive;
|
|
UpdateDeadGWState();
|
|
// turn on dead gw flag to tell findrte not to consider this rte
|
|
|
|
RTE->rte_flags |= RTE_DEADGW;
|
|
RTE->rte_fromdg->rte_flags |= RTE_DEADGW;
|
|
|
|
RTE = FindRTE(Dest, Src, 0,
|
|
DEFAULT_ROUTE_PRI,
|
|
DEFAULT_ROUTE_PRI, UnicastIf);
|
|
|
|
OldRTE->rte_flags &= ~RTE_DEADGW;
|
|
OldRTE->rte_fromdg->rte_flags &= ~RTE_DEADGW;
|
|
|
|
if (RTE == NULL) {
|
|
// No more default gateways! This is bad.
|
|
//ASSERT(FALSE);
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"No more def routes!\n"));
|
|
|
|
OldRTE->rte_fromdg->rte_todg = NULL;
|
|
OldRTE->rte_fromdg->rte_fromdg = NULL;
|
|
|
|
OldRTE->rte_fromdg = NULL;
|
|
|
|
OldRTE->rte_todg = NULL;
|
|
|
|
ValidateDefaultGWs(NULL_IP_ADDR);
|
|
|
|
//ASSERT(DefGWActive == DefGWConfigured);
|
|
|
|
} else {
|
|
|
|
// we have a third gateway to try!
|
|
|
|
// ASSERT(RTE->rte_mask == DEFAULT_MASK);
|
|
|
|
//Treat OldRTE as dead!
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Trying next def route %x\n",RTE));
|
|
|
|
OldRTE->rte_flags &= ~RTE_VALID;
|
|
|
|
RTE->rte_fromdg = OldRTE->rte_fromdg;
|
|
RTE->rte_fromdg->rte_todg = RTE;
|
|
|
|
if (OldRTE->rte_todg)
|
|
OldRTE->rte_todg->rte_fromdg = NULL;
|
|
|
|
OldRTE->rte_todg = NULL;
|
|
OldRTE->rte_fromdg = NULL;
|
|
|
|
//Attach all the RCEs to the new one
|
|
|
|
AttachRCEToNewRTE(RTE, NULL, OldRTE);
|
|
RTE->rte_valid = Now;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//find the next potential default gateway
|
|
RouteTableEntry *OldRTE = RTE;
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Finding potential GW\n" ));
|
|
|
|
OldRTE->rte_flags |= RTE_DEADGW;
|
|
|
|
RTE = FindRTE(Dest, Src, 0,
|
|
DEFAULT_ROUTE_PRI,
|
|
DEFAULT_ROUTE_PRI, UnicastIf);
|
|
|
|
OldRTE->rte_flags &= ~RTE_DEADGW;
|
|
|
|
if (RTE == NULL) {
|
|
// No more default gateways! This is bad.
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," ---No more def routes!\n"));
|
|
// ASSERT(FALSE);
|
|
ValidateDefaultGWs(NULL_IP_ADDR);
|
|
//ASSERT(DefGWActive == DefGWConfigured);
|
|
} else {
|
|
ASSERT(RTE->rte_mask == DEFAULT_MASK);
|
|
|
|
//remember the new gw until we transition fully
|
|
|
|
OldRTE->rte_todg = RTE;
|
|
RTE->rte_fromdg = OldRTE;
|
|
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"FoundGW %x\n",RTE));
|
|
|
|
//Attach this RCE to use the new RTE
|
|
|
|
AttachRCEToNewRTE(RTE, RCE, OldRTE);
|
|
|
|
RTE->rte_valid = Now;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
}
|
|
}
|
|
|
|
//** FindRCE - Find an RCE on an RTE.
|
|
//
|
|
// A routine to find an RCE that's chained on an RTE. We assume the lock
|
|
// is held on the RTE.
|
|
//
|
|
// Entry: RTE - RTE to search.
|
|
// Dest - Destination address of RTE to find.
|
|
// Src - Source address of RTE to find.
|
|
//
|
|
// Returns: Pointer to RTE found, or NULL.
|
|
//
|
|
RouteCacheEntry *
|
|
FindRCE(RouteTableEntry * RTE, IPAddr Dest, IPAddr Src)
|
|
{
|
|
RouteCacheEntry *CurrentRCE;
|
|
|
|
ASSERT(!IP_ADDR_EQUAL(Src, NULL_IP_ADDR));
|
|
for (CurrentRCE = RTE->rte_rcelist; CurrentRCE != NULL;
|
|
CurrentRCE = CurrentRCE->rce_next) {
|
|
if (IP_ADDR_EQUAL(CurrentRCE->rce_dest, Dest) &&
|
|
IP_ADDR_EQUAL(CurrentRCE->rce_src, Src)) {
|
|
break;
|
|
}
|
|
}
|
|
return CurrentRCE;
|
|
|
|
}
|
|
|
|
|
|
//** OpenRCE - Open an RCE for a specific route.
|
|
//
|
|
// Called by the upper layer to open an RCE. We look up the type of the address
|
|
// - if it's invalid, we return 'Destination invalid'. If not, we look up the
|
|
// route, fill in the RCE, and link it on the correct RTE.
|
|
//
|
|
// As an added bonus, this routine will return the local address to use
|
|
// to reach the destination.
|
|
//
|
|
// Entry: Address - Address for which we are to open an RCE.
|
|
// Src - Preferred source address to use.
|
|
// RCE - Pointer to where to return pointer to RCE.
|
|
// Type - Pointer to where to return destination type.
|
|
// MSS - Pointer to where to return MSS for route.
|
|
// OptInfo - Pointer to option information, such as TOS and
|
|
// any source routing info.
|
|
//
|
|
// Returns: Source IP address to use. This will be NULL_IP_ADDR if the
|
|
// specified destination is unreachable for any reason.
|
|
//
|
|
IPAddr
|
|
OpenRCE(IPAddr Address, IPAddr Src, RouteCacheEntry ** RCE, uchar * Type,
|
|
ushort * MSS, IPOptInfo * OptInfo)
|
|
{
|
|
RouteTableEntry *RTE; // Pointer to RTE to put RCE on.
|
|
CTELockHandle TableLock;
|
|
uchar LocalType;
|
|
NetTableEntry *RealNTE = NULL;
|
|
uint ConstrainIF = 0;
|
|
|
|
if (!IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR))
|
|
Address = OptInfo->ioi_addr;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &TableLock);
|
|
|
|
LocalType = GetAddrType(Address);
|
|
|
|
*Type = LocalType;
|
|
|
|
// If the specified address isn't invalid, continue.
|
|
if (LocalType != DEST_INVALID) {
|
|
RouteCacheEntry *NewRCE;
|
|
|
|
// If he's specified a source address, loop through the NTE table
|
|
// now and make sure it's valid.
|
|
if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) {
|
|
NetTableEntry *NTE;
|
|
|
|
NetTableEntry *NetTableList = NewNetTableList[NET_TABLE_HASH(Src)];
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
|
|
if ((NTE->nte_flags & NTE_VALID) &&
|
|
IP_ADDR_EQUAL(Src, NTE->nte_addr))
|
|
break;
|
|
|
|
if (NTE == NULL) {
|
|
// Didn't find a match.
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
// Decide whether to do a strong or weak host lookup
|
|
// No need to do this in case of unidirectional adapter.
|
|
// On unidirectional adapter sends are not permitted.
|
|
// If this openrce is called before setting specific mcast
|
|
// Address (ioi_mcastif) GetIfConstraint for mcast will fail.
|
|
// For W9x backward compatibility reasons, we will let
|
|
// OpenRce succeed even if ioi_mcast if is not set, as an
|
|
// exception in the case of unidirectional adapter. Side effect
|
|
// of this will be - when a send is attempted on this endpoint
|
|
// with this cached rce, it will go out on a random interface.
|
|
//
|
|
|
|
if (!(NTE->nte_if->if_flags & IF_FLAGS_UNI)) {
|
|
ConstrainIF = GetIfConstraint(Address, Src, OptInfo, FALSE);
|
|
}
|
|
|
|
if ((ConstrainIF != 0) && (ConstrainIF != INVALID_IF_INDEX) &&
|
|
(NTE->nte_if->if_index != ConstrainIF)) {
|
|
//
|
|
// The caller requested a strong host lookup, but passed
|
|
// an address on a different interface as the preferred
|
|
// source address. Since we cannot honor this preference
|
|
// for a strong host lookup, we'll ignore the preferred
|
|
// source address, and just choose one from the outgoing
|
|
// interface.
|
|
//
|
|
Src = NULL_IP_ADDR;
|
|
}
|
|
|
|
} else {
|
|
ConstrainIF = GetIfConstraint(Address, Src, OptInfo, FALSE);
|
|
}
|
|
|
|
|
|
// Find the route for this guy. If we can't find one, return NULL.
|
|
if (IP_LOOPBACK_ADDR(Src)) {
|
|
|
|
RTE = LookupRTE(Src, Src, HOST_ROUTE_PRI, ConstrainIF);
|
|
|
|
if (RTE) {
|
|
ASSERT(RTE->rte_if == &LoopInterface);
|
|
} else {
|
|
KdPrint(("No Loopback rte!\n"));
|
|
ASSERT(0);
|
|
}
|
|
|
|
} else {
|
|
RTE = LookupRTE(Address, Src, HOST_ROUTE_PRI, ConstrainIF);
|
|
}
|
|
|
|
if (RTE != (RouteTableEntry *) NULL) {
|
|
CTELockHandle RCEHandle;
|
|
RouteCacheEntry *OldRCE;
|
|
|
|
//
|
|
// Make sure interface is not shutting down.
|
|
//
|
|
if (IS_IF_INVALID(RTE->rte_if) && RTE->rte_if->if_ntecount) {
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
|
|
if (OptInfo->ioi_uni) {
|
|
|
|
//LookupRTE returns first route n the chain of
|
|
//unnumbered ifs.
|
|
//if this is not the one desired, scan further
|
|
|
|
RouteTableEntry *tmpRTE = RTE;
|
|
|
|
while (tmpRTE && (tmpRTE->rte_if->if_index != OptInfo->ioi_uni)) {
|
|
tmpRTE = tmpRTE->rte_next;
|
|
}
|
|
|
|
if (!tmpRTE) {
|
|
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"OpenRCE:No matching unnumbered interface %d\n", OptInfo->ioi_uni));
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
} else
|
|
RTE = tmpRTE;
|
|
}
|
|
|
|
// We found one.
|
|
|
|
// if the route is on a P2MP interface get the mtu from the link associated with the route
|
|
if (RTE->rte_link)
|
|
*MSS = (ushort) MIN(RTE->rte_mtu,RTE->rte_link->link_mtu);
|
|
else
|
|
*MSS = (ushort) RTE->rte_mtu; // Return the route MTU.
|
|
|
|
|
|
if (IP_LOOPBACK_ADDR(Src) && (RTE->rte_if != &LoopInterface)) {
|
|
// The upper layer is sending from a loopback address, but the
|
|
// destination isn't reachable through the loopback interface.
|
|
// Fail the request.
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
// We have the RTE. Fill in the RCE, and link it on the RTE.
|
|
if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL))
|
|
*Type |= DEST_OFFNET_BIT; // Tell upper layer it's off
|
|
// net.
|
|
|
|
//
|
|
// If no source address was specified, then use the best address
|
|
// for the interface. This will generally prevent dynamic NTE's from
|
|
// being chosen as the source for wildcard binds.
|
|
//
|
|
if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) {
|
|
|
|
if (LocalType == DEST_LOCAL) {
|
|
Src = Address;
|
|
RealNTE = LoopNTE;
|
|
} else {
|
|
NetTableEntry *SrcNTE;
|
|
|
|
if ((RTE->rte_if->if_flags & IF_FLAGS_NOIPADDR) && (IP_ADDR_EQUAL(RTE->rte_if->if_nte->nte_addr, NULL_IP_ADDR))) {
|
|
|
|
Src = g_ValidAddr;
|
|
if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) {
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
} else {
|
|
|
|
// This is routelookup for outgoing packet
|
|
// Check for non-transient address availability
|
|
|
|
SrcNTE = BestNTEForIF(
|
|
ADDR_FROM_RTE(RTE, Address),
|
|
RTE->rte_if,
|
|
TRUE
|
|
);
|
|
|
|
if (SrcNTE == NULL) {
|
|
// Can't find an address! Fail the request.
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
Src = SrcNTE->nte_addr;
|
|
}
|
|
}
|
|
}
|
|
// Now, see if an RCE already exists for this.
|
|
|
|
if (RCE == NULL) {
|
|
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Openrce with null RCE!! %x\n",Src));
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return Src;
|
|
}
|
|
|
|
if ((OldRCE = FindRCE(RTE, Address, Src)) == NULL) {
|
|
|
|
// Don't have an existing RCE. See if we can get a new one,
|
|
// and fill it in.
|
|
|
|
NewRCE = CTEAllocMemNBoot(sizeof(RouteCacheEntry), 'AiCT');
|
|
*RCE = NewRCE;
|
|
|
|
if (NewRCE != NULL) {
|
|
RtlZeroMemory(NewRCE, sizeof(RouteCacheEntry));
|
|
|
|
NewRCE->rce_src = Src;
|
|
NewRCE->rce_dtype = LocalType;
|
|
NewRCE->rce_cnt = 1;
|
|
CTEInitLock(&NewRCE->rce_lock);
|
|
NewRCE->rce_dest = Address;
|
|
NewRCE->rce_rte = RTE;
|
|
NewRCE->rce_flags = RCE_VALID;
|
|
if (RTE->rte_flags & RTE_IF_VALID) {
|
|
NewRCE->rce_flags |= RCE_CONNECTED;
|
|
//* Update the ref. count for this interface.
|
|
NewRCE->rce_flags |= RCE_REFERENCED;
|
|
LOCKED_REFERENCE_IF(RTE->rte_if);
|
|
// We register the chksum capability of the interface
|
|
// associated with this RCE, because interface definitions
|
|
// are transparent to TCP or UDP.
|
|
|
|
if (!IPSecStatus) {
|
|
|
|
NewRCE->rce_OffloadFlags = RTE->rte_if->if_OffloadFlags;
|
|
} else {
|
|
|
|
NewRCE->rce_OffloadFlags = 0;
|
|
}
|
|
|
|
NewRCE->rce_TcpLargeSend.MaxOffLoadSize = RTE->rte_if->if_MaxOffLoadSize;
|
|
NewRCE->rce_TcpLargeSend.MinSegmentCount = RTE->rte_if->if_MaxSegments;
|
|
|
|
NewRCE->rce_TcpWindowSize = RTE->rte_if->if_TcpWindowSize;
|
|
NewRCE->rce_TcpInitialRTT = RTE->rte_if->if_TcpInitialRTT;
|
|
NewRCE->rce_TcpDelAckTicks = RTE->rte_if->if_TcpDelAckTicks;
|
|
NewRCE->rce_TcpAckFrequency = RTE->rte_if->if_TcpAckFrequency;
|
|
NewRCE->rce_mediaspeed = RTE->rte_if->if_speed;
|
|
} //RTE_IF_VALID
|
|
|
|
NewRCE->rce_next = RTE->rte_rcelist;
|
|
RTE->rte_rcelist = NewRCE;
|
|
|
|
RTE->rte_rces++;
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
|
|
return Src;
|
|
} else {
|
|
// alloc failed
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
|
|
return NULL_IP_ADDR;
|
|
}
|
|
|
|
} else {
|
|
// We have an existing RCE. We'll return his source as the
|
|
// valid source, bump the reference count, free the locks
|
|
// and return.
|
|
CTEGetLock(&OldRCE->rce_lock, &RCEHandle);
|
|
OldRCE->rce_cnt++;
|
|
*RCE = OldRCE;
|
|
|
|
if (OldRCE->rce_newmtu) {
|
|
*MSS = (USHORT) OldRCE->rce_newmtu;
|
|
}
|
|
OldRCE->rce_rte->rte_rces++;
|
|
|
|
CTEFreeLock(&OldRCE->rce_lock, RCEHandle);
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return Src;
|
|
}
|
|
} else {
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
}
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
|
|
void
|
|
FreeRCEToList(RouteCacheEntry * RCE)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free RCE to the RCEFreeList (since the use_cnt on it is non zero)
|
|
Called with routetable lock held
|
|
Arguments:
|
|
|
|
RCE : RCE to free
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
// link this new interface at the front of the list
|
|
|
|
RCE->rce_next = RCEFreeList;
|
|
RCEFreeList = RCE;
|
|
|
|
return;
|
|
}
|
|
|
|
//* CloseRCE - Close an RCE.
|
|
//
|
|
// Called by the upper layer when it wants to close the RCE. We unlink it from
|
|
// the RTE.
|
|
//
|
|
// Entry: RCE - Pointer to the RCE to be closed.
|
|
//
|
|
// Exit: Nothing.
|
|
//
|
|
void
|
|
CloseRCE(RouteCacheEntry * RCE)
|
|
{
|
|
RouteTableEntry *RTE; // Route on which RCE is linked.
|
|
RouteCacheEntry *PrevRCE;
|
|
CTELockHandle TableLock; // Lock handles used.
|
|
Interface *IF;
|
|
Interface *tmpif = NULL;
|
|
uint FreetoRCEFreeList = 0;
|
|
|
|
if (RCE != NULL) {
|
|
CTEGetLock(&RouteTableLock.Lock, &TableLock);
|
|
CTEGetLockAtDPC(&RCE->rce_lock);
|
|
|
|
if ((RCE->rce_flags & RCE_VALID) && !(RCE->rce_flags & RCE_LINK_DELETED)) {
|
|
RCE->rce_rte->rte_rces--;
|
|
}
|
|
|
|
if (--RCE->rce_cnt == 0) {
|
|
// ASSERT(RCE->rce_usecnt == 0);
|
|
ASSERT(*(int *)&(RCE->rce_usecnt) >= 0);
|
|
if ((RCE->rce_flags & RCE_VALID) && !(RCE->rce_flags & RCE_LINK_DELETED)) {
|
|
|
|
// The RCE is valid, so we have a valid RTE in the pointer
|
|
// field. Walk down the RTE rcelist, looking for this guy.
|
|
|
|
RTE = RCE->rce_rte;
|
|
tmpif = IF = RTE->rte_if;
|
|
|
|
PrevRCE = STRUCT_OF(RouteCacheEntry, &RTE->rte_rcelist,
|
|
rce_next);
|
|
|
|
// Walk down the list until we find him.
|
|
while (PrevRCE != NULL) {
|
|
if (PrevRCE->rce_next == RCE)
|
|
break;
|
|
PrevRCE = PrevRCE->rce_next;
|
|
}
|
|
|
|
ASSERT(PrevRCE != NULL);
|
|
|
|
if(PrevRCE) {
|
|
|
|
PrevRCE->rce_next = RCE->rce_next;
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
//Make sure if the interface pointed by RCE
|
|
//is still there
|
|
tmpif = IFList;
|
|
|
|
IF = (Interface *) RCE->rce_rte;
|
|
|
|
while (tmpif) {
|
|
|
|
if (tmpif == IF)
|
|
break;
|
|
tmpif = tmpif->if_next;
|
|
}
|
|
|
|
}
|
|
|
|
if (tmpif) {
|
|
|
|
if (RCE->rce_flags & RCE_CONNECTED) {
|
|
(*(IF->if_invalidate)) (IF->if_lcontext, RCE);
|
|
} else {
|
|
UnConnected++;
|
|
UnConnectedRCE = RCE;
|
|
(*(IF->if_invalidate)) (IF->if_lcontext, RCE);
|
|
}
|
|
|
|
if (RCE->rce_usecnt != 0) {
|
|
// free to the free list
|
|
// and check in timer if the usecnt has fallen to 0, if yes free it
|
|
FreetoRCEFreeList = 1;
|
|
} else {
|
|
if (RCE->rce_flags & RCE_REFERENCED) {
|
|
LockedDerefIF(IF);
|
|
}
|
|
}
|
|
|
|
CTEFreeLockFromDPC(&RCE->rce_lock);
|
|
|
|
if (FreetoRCEFreeList) {
|
|
RCE->rce_rte = (RouteTableEntry *) IF;
|
|
FreeRCEToList(RCE);
|
|
} else {
|
|
CTEFreeMem(RCE);
|
|
}
|
|
|
|
} else { //tmpif==NULL
|
|
|
|
CTEFreeLockFromDPC(&RCE->rce_lock);
|
|
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
|
|
} else {
|
|
CTEFreeLockFromDPC(&RCE->rce_lock);
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
}
|
|
}
|
|
}
|
|
|
|
//* LockedAddRoute - Add a route to the routing table.
|
|
//
|
|
// Called by AddRoute to add a route to the routing table. We assume the
|
|
// route table lock is already held. If the route to be added already exists
|
|
// we update it. Routes are identified by a (Destination, Mask, FirstHop,
|
|
// Interface) tuple. If an exact match exists we'll update the metric, which
|
|
// may cause us to promote RCEs from other RTEs, or we may be demoted in which
|
|
// case we'll invalidate our RCEs and let them be reassigned at transmission
|
|
// time.
|
|
//
|
|
// If we have to create a new RTE we'll do so, and find the best previous
|
|
// RTE, and promote RCEs from that one to the new one.
|
|
//
|
|
// The route table is an open hash structure. Within each hash chain the
|
|
// RTEs with the longest masks (the 'priority') come first, and within
|
|
// each priority the RTEs with the smallest metric come first.
|
|
//
|
|
// Entry: Destination - Destination address for which route is being added.
|
|
// Mask - Mask for destination.
|
|
// FirstHop - First hop for address. Could be IPADDR_LOCAL.
|
|
// OutIF - Pointer to outgoing I/F.
|
|
// MTU - Maximum MTU for this route.
|
|
// Metric - Metric for this route.
|
|
// Proto - Protocol type to store in route.
|
|
// AType - Administrative type of route.
|
|
// Context - context to be associated with the route
|
|
// SetWithRefcnt - indicates the route should be referenced
|
|
// on the creator's behalf.
|
|
// RNO - optionally supplies a route-notification structure
|
|
// to be filled on output with details for the new route
|
|
//
|
|
// Returns: Status of attempt to add route.
|
|
//
|
|
IP_STATUS
|
|
LockedAddRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop,
|
|
Interface * OutIF, uint MTU, uint Metric, uint Proto, uint AType,
|
|
ROUTE_CONTEXT Context, BOOLEAN SetWithRefcnt,
|
|
IPRouteNotifyOutput* RNO)
|
|
{
|
|
uint RouteType; // SNMP route type.
|
|
RouteTableEntry *NewRTE; // Entries for new and previous RTEs.
|
|
uint OldMetric; // Previous metric in use.
|
|
uint OldPriority; // Priority of previous route to destination.
|
|
uint Now = CTESystemUpTime() / 1000L; // System up time,
|
|
// in seconds.
|
|
ushort OldFlags;
|
|
Interface *OldIF = NULL;
|
|
ULONG status;
|
|
ULONG matchFlags;
|
|
RouteTableEntry *pOldBestRTE;
|
|
RouteTableEntry *pNewBestRTE;
|
|
|
|
LinkEntry *Link;
|
|
|
|
IPAddr AllSNBCast;
|
|
IPMask TmpMask;
|
|
|
|
// OutIF is ref'd so it can't go away
|
|
|
|
Link = OutIF->if_link;
|
|
|
|
|
|
// If Metric is 0, set the metric to interface metric
|
|
|
|
if (Metric == 0) {
|
|
Metric = OutIF->if_metric;
|
|
}
|
|
|
|
|
|
// Do the following only if the interface is not a dummy interface
|
|
|
|
if (OutIF != (Interface *) & DummyInterface) {
|
|
// Check we are adding a multicast route
|
|
|
|
if (IP_ADDR_EQUAL(Destination, MCAST_DEST) &&
|
|
(OutIF->if_iftype & DONT_ALLOW_MCAST))
|
|
return IP_SUCCESS;
|
|
|
|
if (OutIF->if_iftype & DONT_ALLOW_UCAST) {
|
|
|
|
// Check whether we are adding a ucast route
|
|
|
|
TmpMask = IPNetMask(OutIF->if_nte->nte_addr);
|
|
AllSNBCast =
|
|
(OutIF->if_nte->nte_addr & TmpMask) |
|
|
(OutIF->if_bcast & ~TmpMask);
|
|
if (!(IP_ADDR_EQUAL(Destination, OutIF->if_bcast) ||
|
|
IP_ADDR_EQUAL(Destination, AllSNBCast) ||
|
|
IP_ADDR_EQUAL(Destination, MCAST_DEST))) {
|
|
// this is not a bcast/mcast route: this is a ucast route
|
|
return IP_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
// First do some consistency checks. Make sure that the Mask and
|
|
// Destination agree.
|
|
if (!IP_ADDR_EQUAL(Destination & Mask, Destination))
|
|
return IP_BAD_DESTINATION;
|
|
|
|
if (AType != ATYPE_PERM && AType != ATYPE_OVERRIDE && AType != ATYPE_TEMP)
|
|
return IP_BAD_REQ;
|
|
|
|
// If the interface is marked as going away, fail this.
|
|
if (OutIF->if_flags & IF_FLAGS_DELETING) {
|
|
return IP_BAD_REQ;
|
|
}
|
|
|
|
RouteType = IP_ADDR_EQUAL(FirstHop, IPADDR_LOCAL) ? IRE_TYPE_DIRECT :
|
|
IRE_TYPE_INDIRECT;
|
|
|
|
// If this is a route that is being added on an interface that has no
|
|
// IP address, mark this as IRE_TYPE_DIRECT. This is true only for
|
|
// P2P or P2MP interface, where route is plumbed and then address
|
|
// is added due to a perf reason.
|
|
|
|
|
|
if (((OutIF->if_flags & IF_FLAGS_P2P) ||
|
|
(OutIF->if_flags & IF_FLAGS_P2MP)) &&
|
|
OutIF->if_nte && (OutIF->if_nte->nte_flags & NTE_VALID) &&
|
|
(IP_ADDR_EQUAL(OutIF->if_nte->nte_addr,NULL_IP_ADDR))) {
|
|
RouteType = IRE_TYPE_DIRECT;
|
|
}
|
|
|
|
MTU = MAX(MTU, MIN_VALID_MTU);
|
|
|
|
// If the outgoing interface has NTEs attached but none are valid, fail
|
|
// this request unless it's a request to add the broadcast route.
|
|
if (OutIF != (Interface *) & DummyInterface) {
|
|
if (OutIF->if_ntecount == 0 && OutIF->if_nte != NULL &&
|
|
!IP_ADDR_EQUAL(Destination, OutIF->if_bcast) &&
|
|
!(OutIF->if_flags & IF_FLAGS_NOIPADDR)) {
|
|
// This interface has NTEs attached, but none are valid. Fail the
|
|
// request.
|
|
return IP_BAD_REQ;
|
|
}
|
|
}
|
|
if (OutIF->if_flags & IF_FLAGS_P2MP) {
|
|
|
|
while (Link) {
|
|
if ((Link->link_NextHop == FirstHop) ||
|
|
((Link->link_NextHop == Destination) &&
|
|
(FirstHop == IPADDR_LOCAL))) {
|
|
break;
|
|
}
|
|
Link = Link->link_next;
|
|
}
|
|
|
|
if (!Link)
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_IP && DBG_ROUTE,
|
|
(DTEXT("LockedAddRoute: D = %08x, M = %08x, NH = %08x, IF = %08x\n")
|
|
DTEXT("\t\tMTU = %x, Met = %08x, Prot = %08x, AT = %08x, C = %08x\n"),
|
|
Destination, Mask, FirstHop, OutIF, MTU, Metric, Proto, AType,
|
|
Context));
|
|
|
|
// Insert the route in the proper place depending on the dest, metric
|
|
// Match next-hop (and interface if not a demand-dial route)
|
|
matchFlags = MATCH_NHOP;
|
|
|
|
if (!Context) {
|
|
matchFlags |= MATCH_INTF;
|
|
}
|
|
status = InsRoute(Destination, Mask, FirstHop, OutIF, Metric,
|
|
matchFlags, &NewRTE, &pOldBestRTE, &pNewBestRTE);
|
|
|
|
if (status != IP_SUCCESS) {
|
|
return status;
|
|
}
|
|
// Has a best route been replaced
|
|
if ((pOldBestRTE) && (pOldBestRTE != pNewBestRTE)) {
|
|
InvalidateRCEChain(pOldBestRTE);
|
|
|
|
// If the replaced route is a default gateway,
|
|
// we may need to switch connections to the new entry.
|
|
// To do so, we retrieve the current default gateway,
|
|
// invalidate all its RCEs, and revalidate all gateways
|
|
// to restart the dead-gateway detection procedure.
|
|
|
|
if (pOldBestRTE->rte_mask == DEFAULT_MASK) {
|
|
ValidateDefaultGWs(NULL_IP_ADDR);
|
|
}
|
|
}
|
|
|
|
// Copy old route's parameters now
|
|
OldFlags = NewRTE->rte_flags;
|
|
|
|
if (!(NewRTE->rte_flags & RTE_NEW)) {
|
|
|
|
OldMetric = NewRTE->rte_metric;
|
|
OldPriority = NewRTE->rte_priority;
|
|
OldIF = NewRTE->rte_if;
|
|
|
|
if (Metric >= OldMetric && (OldFlags & RTE_VALID)) {
|
|
InvalidateRCEChain(NewRTE);
|
|
}
|
|
if (SetWithRefcnt) {
|
|
ASSERT(NewRTE->rte_refcnt > 0);
|
|
NewRTE->rte_refcnt++;
|
|
}
|
|
} else {
|
|
// this is a new RTE
|
|
NewRTE->rte_refcnt = 1;
|
|
}
|
|
|
|
// If this is P2MP, chain this RTE on link
|
|
if (Link && (NewRTE->rte_link == NULL)) {
|
|
|
|
//
|
|
// This RTE is not on the link
|
|
// Insert the route in the linkrte chain
|
|
//
|
|
|
|
NewRTE->rte_nextlinkrte = Link->link_rte;
|
|
Link->link_rte = NewRTE;
|
|
NewRTE->rte_link = Link;
|
|
}
|
|
|
|
|
|
// Update fields in the new/old route
|
|
NewRTE->rte_addr = FirstHop;
|
|
NewRTE->rte_mtu = MTU;
|
|
NewRTE->rte_metric = Metric;
|
|
NewRTE->rte_type = (ushort) RouteType;
|
|
NewRTE->rte_if = OutIF;
|
|
|
|
NewRTE->rte_flags &= ~RTE_NEW;
|
|
NewRTE->rte_flags |= RTE_VALID;
|
|
NewRTE->rte_flags &= ~RTE_INCREASE;
|
|
if (OutIF != (Interface *) & DummyInterface) {
|
|
NewRTE->rte_flags |= RTE_IF_VALID;
|
|
SortRoutesInDestByRTE(NewRTE);
|
|
} else
|
|
NewRTE->rte_flags &= ~RTE_IF_VALID;
|
|
|
|
NewRTE->rte_admintype = AType;
|
|
NewRTE->rte_proto = Proto;
|
|
NewRTE->rte_valid = Now;
|
|
NewRTE->rte_mtuchange = Now;
|
|
NewRTE->rte_context = Context;
|
|
|
|
|
|
// Check if this is a new route or an old one
|
|
if (OldFlags & RTE_NEW) {
|
|
// Reset few fields in new route
|
|
|
|
NewRTE->rte_todg = NULL;
|
|
NewRTE->rte_fromdg = NULL;
|
|
NewRTE->rte_rces = 0;
|
|
|
|
RtlZeroMemory(NewRTE->rte_arpcontext, sizeof(RCE_CONTEXT_SIZE));
|
|
|
|
IPSInfo.ipsi_numroutes++;
|
|
|
|
if (NewRTE->rte_mask == DEFAULT_MASK) {
|
|
// A default route.
|
|
DefGWConfigured++;
|
|
DefGWActive++;
|
|
UpdateDeadGWState();
|
|
}
|
|
} else {
|
|
|
|
// If the RTE is for a default gateway and the old flags indicate
|
|
// he wasn't valid then we're essentially creating a new active
|
|
// default gateway. So bump up the active default gateway count.
|
|
if (NewRTE->rte_mask == DEFAULT_MASK) {
|
|
if (!(OldFlags & RTE_VALID)) {
|
|
DefGWActive++;
|
|
UpdateDeadGWState();
|
|
|
|
// Reset few fields in this route
|
|
|
|
NewRTE->rte_todg = NULL;
|
|
NewRTE->rte_fromdg = NULL;
|
|
NewRTE->rte_rces = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If a route-notification structure was supplied, fill it in.
|
|
|
|
if (RNO) {
|
|
RNO->irno_dest = NewRTE->rte_dest;
|
|
RNO->irno_mask = NewRTE->rte_mask;
|
|
RNO->irno_nexthop = GetNextHopForRTE(NewRTE);
|
|
RNO->irno_proto = NewRTE->rte_proto;
|
|
RNO->irno_ifindex = OutIF->if_index;
|
|
RNO->irno_metric = NewRTE->rte_metric;
|
|
if (OldFlags & RTE_NEW) {
|
|
RNO->irno_flags = IRNO_FLAG_ADD;
|
|
}
|
|
}
|
|
|
|
return IP_SUCCESS;
|
|
}
|
|
|
|
//* AddRoute - Add a route to the routing table.
|
|
//
|
|
// This is just a shell for the real add route routine. All we do is take
|
|
// the route table lock, and call the LockedAddRoute routine to deal with
|
|
// the request. This is done this way because there are certain routines that
|
|
// need to be able to atomically examine and add routes.
|
|
//
|
|
// Entry: Destination - Destination address for which route is being
|
|
// added.
|
|
// Mask - Mask for destination.
|
|
// FirstHop - First hop for address. Could be IPADDR_LOCAL.
|
|
// OutIF - Pointer to outgoing I/F.
|
|
// MTU - Maximum MTU for this route.
|
|
// Metric - Metric for this route.
|
|
// Proto - Protocol type to store in route.
|
|
// AType - Administrative type of route.
|
|
// Context - Context for this route.
|
|
//
|
|
// Returns: Status of attempt to add route.
|
|
//
|
|
IP_STATUS
|
|
AddRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop,
|
|
Interface * OutIF, uint MTU, uint Metric, uint Proto, uint AType,
|
|
ROUTE_CONTEXT Context, uint Flags)
|
|
{
|
|
CTELockHandle TableHandle;
|
|
IP_STATUS Status;
|
|
BOOLEAN SkipExNotifyQ = FALSE;
|
|
IPRouteNotifyOutput RNO = {0};
|
|
|
|
if ((Flags & RT_EXCLUDE_LOCAL) && Proto == IRE_PROTO_LOCAL) {
|
|
return IP_BAD_REQ;
|
|
}
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &TableHandle);
|
|
|
|
if (Flags & RT_NO_NOTIFY) {
|
|
SkipExNotifyQ = TRUE;
|
|
}
|
|
Status = LockedAddRoute(Destination, Mask, FirstHop, OutIF, MTU, Metric,
|
|
Proto, AType, Context,
|
|
(BOOLEAN)((Flags & RT_REFCOUNT) ? TRUE : FALSE),
|
|
&RNO);
|
|
|
|
if (Status == IP_SUCCESS) {
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, TableHandle);
|
|
|
|
#if FFP_SUPPORT
|
|
FFPFlushRequired = TRUE;
|
|
#endif
|
|
|
|
// Under certain conditions, LockedAddRoute returns IP_SUCCESS
|
|
// even though no route was added. We catch such cases by examining
|
|
// the interface index on output which, for true additions, should
|
|
// always be non-zero.
|
|
|
|
if (RNO.irno_ifindex) {
|
|
if (!SkipExNotifyQ) {
|
|
RtChangeNotifyEx(&RNO);
|
|
}
|
|
|
|
RtChangeNotify(&RNO);
|
|
}
|
|
} else {
|
|
CTEFreeLock(&RouteTableLock.Lock, TableHandle);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
//* RtChangeNotify - Supply a route-change for notification to any clients
|
|
//
|
|
// This routine is a shell around the address-/route-change notification
|
|
// handler. It unpacks information about the changed route, and passes it
|
|
// to the common handler specifying the route-change notification queue
|
|
// as the source for pending client-requests.
|
|
//
|
|
// Entry: RNO - describes the route-notification event
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
RtChangeNotify(IPRouteNotifyOutput *RNO)
|
|
{
|
|
ChangeNotify((IPNotifyOutput *)RNO, &RtChangeNotifyQueue,
|
|
&RouteTableLock.Lock);
|
|
}
|
|
|
|
//* RtChangeNotifyEx - Supply a route-change for notification to any clients
|
|
//
|
|
// This routine is a shell around the address-/route-change notification
|
|
// handler. It unpacks information about the changed route, and passes it
|
|
// to the common handler specifying the extended route-change notification
|
|
// queue as the source for pending client-requests.
|
|
//
|
|
// Entry: RNO - describes the route-notification event
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
RtChangeNotifyEx(IPRouteNotifyOutput *RNO)
|
|
{
|
|
ChangeNotify((IPNotifyOutput *)RNO, &RtChangeNotifyQueueEx,
|
|
&RouteTableLock.Lock);
|
|
}
|
|
|
|
//* ChangeNotifyAsync - Supply a change for notification
|
|
//
|
|
// This routine is a handler for a deferred change-notification. It unpacks
|
|
// information about the change, and passes it to the common handler.
|
|
//
|
|
// Entry: Event - CTEEvent for the deferred call
|
|
// Context - context containing information about the change
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
ChangeNotifyAsync(CTEEvent *Event, PVOID Context)
|
|
{
|
|
ChangeNotifyEvent *CNE = (ChangeNotifyEvent *)Context;
|
|
|
|
UNREFERENCED_PARAMETER(Event);
|
|
|
|
ChangeNotify(&CNE->cne_info, CNE->cne_queue, CNE->cne_lock);
|
|
CTEFreeMem(Context);
|
|
}
|
|
|
|
//* ChangeNotifyClientInQueue - See if a client is in a notification queue
|
|
//
|
|
// This is a utility routine called by ChangeNotify to determine
|
|
// if a given client, identified by a file object, has a request
|
|
// in a given notification queue.
|
|
//
|
|
// Entry: FileObject - identifies the client
|
|
// NotifyQueue - contains a list of requests to be searched
|
|
//
|
|
// Returns: TRUE if the client is present, FALSE otherwise.
|
|
//
|
|
BOOLEAN
|
|
ChangeNotifyClientInQueue(PFILE_OBJECT FileObject, PLIST_ENTRY NotifyQueue)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
for (ListEntry = NotifyQueue->Flink; ListEntry != NotifyQueue;
|
|
ListEntry = ListEntry->Flink) {
|
|
Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
if (FileObject == IrpSp->FileObject) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//* ChangeNotify - Notify about a route change
|
|
//
|
|
// This routine is the common handler for change notifications.
|
|
// It takes a description of a change, and searches the specified queue
|
|
// for a pending client-request that corresponds to the changed item.
|
|
//
|
|
// Entry: NotifyOutput - contains information about the change event
|
|
// NotifyQueue - supplies the queue in which to search for clients
|
|
// Lock - supplies the lock protecting 'NotifyQueue'.
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
ChangeNotify(IPNotifyOutput* NotifyOutput, PLIST_ENTRY NotifyQueue, PVOID Lock)
|
|
{
|
|
IPAddr Add = NotifyOutput->ino_addr;
|
|
IPMask Mask = NotifyOutput->ino_mask;
|
|
PIRP Irp;
|
|
CTELockHandle LockHandle;
|
|
PLIST_ENTRY ListEntry;
|
|
PIPNotifyData NotifyData;
|
|
LIST_ENTRY LocalNotifyQueue;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
BOOLEAN synchronizeWithCancelRoutine = FALSE;
|
|
|
|
// See if we're being invoked it dispatch IRQL and, if so,
|
|
// defer the notification to a worker thread.
|
|
//
|
|
// N.B. We do this *without* touching 'Lock' which might already
|
|
// be held by the caller.
|
|
|
|
if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
|
|
ChangeNotifyEvent *CNE;
|
|
CNE = CTEAllocMemNBoot(sizeof(ChangeNotifyEvent), 'xiCT');
|
|
if (CNE) {
|
|
CNE->cne_info = *NotifyOutput;
|
|
CNE->cne_queue = NotifyQueue;
|
|
CNE->cne_lock = Lock;
|
|
CTEInitEvent(&CNE->cne_event, ChangeNotifyAsync);
|
|
CTEScheduleDelayedEvent(&CNE->cne_event, CNE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Examine the list of pending change-notification requeusts
|
|
// to see if any of them match the parameters of the current event.
|
|
|
|
InitializeListHead(&LocalNotifyQueue);
|
|
CTEGetLock(Lock, &LockHandle);
|
|
|
|
for (ListEntry = NotifyQueue->Flink; ListEntry != NotifyQueue; ) {
|
|
|
|
Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
// Determine whether an input buffer was supplied and, if so,
|
|
// pick it up to see if the event matches the notification request.
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
|
|
sizeof(IPNotifyData)) {
|
|
NotifyData = Irp->AssociatedIrp.SystemBuffer;
|
|
} else {
|
|
NotifyData = NULL;
|
|
}
|
|
|
|
// Now determine whether we should consider this IRP at all.
|
|
// We'll normally complete all matching IRPs when an event occurs,
|
|
// but certain clients want only one matching IRP to be completed,
|
|
// so they can maintain a backlog of IRPs to make sure that they don't
|
|
// miss any events. Such clients set 'Synchronization' as the version
|
|
// in their requests.
|
|
|
|
if (NotifyData &&
|
|
NotifyData->Version == IPNotifySynchronization &&
|
|
ChangeNotifyClientInQueue(IrpSp->FileObject, &LocalNotifyQueue)) {
|
|
ListEntry = ListEntry->Flink;
|
|
continue;
|
|
}
|
|
|
|
// If no data was passed or it contains NULL address or an Address that
|
|
// matches the address that was added or deleted, complete the irp
|
|
|
|
if ((NotifyData == NULL) ||
|
|
(NotifyData->Add == 0) ||
|
|
((NotifyData->Add & Mask) == (Add & Mask))) {
|
|
|
|
//
|
|
// We are going to remove the LE, so first save the Flink
|
|
//
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
|
|
if (IoSetCancelRoutine(Irp, NULL) == NULL) {
|
|
synchronizeWithCancelRoutine = TRUE;
|
|
}
|
|
|
|
#if !MILLEN
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
|
|
sizeof(IPNotifyOutput)) {
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, NotifyOutput,
|
|
sizeof(IPNotifyOutput));
|
|
Irp->IoStatus.Information = sizeof(IPNotifyOutput);
|
|
} else {
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
#else // !MILLEN
|
|
// For Millennium, this is only called for RtChange queues now.
|
|
//
|
|
ASSERT(NotifyQueue == &RtChangeNotifyQueue);
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
|
|
sizeof(IP_RTCHANGE_NOTIFY)) {
|
|
PIP_RTCHANGE_NOTIFY pReply = Irp->AssociatedIrp.SystemBuffer;
|
|
pReply->Addr = Add;
|
|
pReply->Mask = Mask;
|
|
Irp->IoStatus.Information = sizeof(IP_RTCHANGE_NOTIFY);
|
|
} else {
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
#endif // MILLEN
|
|
|
|
InsertTailList(&LocalNotifyQueue, &Irp->Tail.Overlay.ListEntry);
|
|
} else {
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(Lock, LockHandle);
|
|
|
|
if (!IsListEmpty(&LocalNotifyQueue)) {
|
|
if (synchronizeWithCancelRoutine) {
|
|
IoAcquireCancelSpinLock(&LockHandle);
|
|
IoReleaseCancelSpinLock(LockHandle);
|
|
}
|
|
do {
|
|
ListEntry = RemoveHeadList(&LocalNotifyQueue);
|
|
Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
} while (!IsListEmpty(&LocalNotifyQueue));
|
|
}
|
|
}
|
|
|
|
//* RtChangeNotifyCancel - cancels a route-change notification request.
|
|
//
|
|
// This routine is a wrapper around the common request-cancelation handler
|
|
// for change-notification requests.
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
RtChangeNotifyCancel(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
|
{
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
CancelNotify(Irp, &RtChangeNotifyQueue, &RouteTableLock.Lock);
|
|
}
|
|
|
|
//* RtChangeNotifyCancelEx - cancels a route-change notification request.
|
|
//
|
|
// This routine is a wrapper around the common request-cancelation handler
|
|
// for change-notification requests.
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
RtChangeNotifyCancelEx(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
|
{
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
CancelNotify(Irp, &RtChangeNotifyQueueEx, &RouteTableLock.Lock);
|
|
}
|
|
|
|
//* CancelNotify - cancels a change-notification request.
|
|
//
|
|
// This routine is the common handler for cancelation of change-notification
|
|
// requests. It searches for the given request in the qiven queue and,
|
|
// if found, completes it immediately with a cancelation status.
|
|
//
|
|
// It is invoked with the I/O cancel spin-lock held by the caller,
|
|
// and frees the cancel spin-lock before returning.
|
|
//
|
|
// Entry: Irp - the I/O request packet for the request
|
|
// NotifyQueue - change-notification queue containing the request
|
|
// Lock - lock protecting 'NotifyQueue'.
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
CancelNotify(PIRP Irp, PLIST_ENTRY NotifyQueue, PVOID Lock)
|
|
{
|
|
CTELockHandle LockHandle;
|
|
PLIST_ENTRY ListEntry;
|
|
BOOLEAN Found = FALSE;
|
|
|
|
CTEGetLock(Lock, &LockHandle);
|
|
for (ListEntry = NotifyQueue->Flink; ListEntry != NotifyQueue;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
if (CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry) == Irp) {
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
CTEFreeLock(Lock, LockHandle);
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
if (Found) {
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
}
|
|
}
|
|
|
|
//* CancelNotifyByContext - cancels a change-notification request.
|
|
//
|
|
// This routine handles user-initiated cancellation of change-notification
|
|
// requests. It searches for a request with the given context in the
|
|
// given queue and, if found, completes it with a cancellation status.
|
|
//
|
|
// It is invoked with the I/O cancel spin-lock held by the caller and,
|
|
// if the request is found, it frees the cancel spin-lock before returning.
|
|
//
|
|
// Entry: FileObject - the file-object on which the user-initiated
|
|
// cancellation was received.
|
|
// Context - the I/O request packet for the request
|
|
// NotifyQueue - change-notification queue containing the request
|
|
// Lock - lock protecting 'NotifyQueue'.
|
|
//
|
|
// Returns: TRUE if the request was found, FALSE otherwise.
|
|
//
|
|
BOOLEAN
|
|
CancelNotifyByContext(PFILE_OBJECT FileObject, PVOID ApcContext,
|
|
PLIST_ENTRY NotifyQueue, PVOID Lock)
|
|
{
|
|
PIRP Irp;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
CTEGetLockAtDPC(Lock);
|
|
for (ListEntry = NotifyQueue->Flink; ListEntry != NotifyQueue;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
|
|
if (Irp->Tail.Overlay.DriverContext[0] == FileObject &&
|
|
Irp->Overlay.AsynchronousParameters.UserApcContext == ApcContext) {
|
|
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
CTEFreeLockFromDPC(Lock);
|
|
IoReleaseCancelSpinLock(DISPATCH_LEVEL);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
CTEFreeLockFromDPC(Lock);
|
|
return FALSE;
|
|
}
|
|
|
|
//* DeleteRoute - Delete a route from the routing table.
|
|
//
|
|
// Called by upper layer or management code to delete a route from the routing
|
|
// table. If we can't find the route we return an error. If we do find it, we
|
|
// remove it, and invalidate any RCEs associated with it. These RCEs will be
|
|
// reassigned the next time they're used. A route is uniquely identified by
|
|
// a (Destination, Mask, FirstHop, Interface) tuple.
|
|
//
|
|
// Entry: Destination - Destination address for which route is being
|
|
// deleted.
|
|
// Mask - Mask for destination.
|
|
// FirstHop - First hop on way to Destination.
|
|
// -1 means route is local.
|
|
// OutIF - Outgoing interface for route.
|
|
// Flags - selects various semantics for deletion.
|
|
//
|
|
// Returns: Status of attempt to delete route.
|
|
//
|
|
IP_STATUS
|
|
DeleteRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop,
|
|
Interface * OutIF, uint Flags)
|
|
{
|
|
RouteTableEntry *RTE; // RTE being deleted.
|
|
CTELockHandle TableLock; // Lock handle for table.
|
|
UINT retval;
|
|
RouteTableEntry *pOldBestRTE;
|
|
RouteTableEntry *pNewBestRTE;
|
|
BOOLEAN DeleteDone = FALSE;
|
|
IPRouteNotifyOutput RNO = {0};
|
|
uint MatchFlags = MATCH_FULL;
|
|
|
|
// Look up the route by calling FindSpecificRTE. If we can't find it,
|
|
// fail the call.
|
|
CTEGetLock(&RouteTableLock.Lock, &TableLock);
|
|
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"DeleteRoute: D = %08x, M = %08x, NH = %08x, IF = %08x\n",
|
|
Destination, Mask, FirstHop, OutIF));
|
|
|
|
if (Flags & RT_EXCLUDE_LOCAL) {
|
|
MatchFlags |= MATCH_EXCLUDE_LOCAL;
|
|
}
|
|
if (Flags & RT_REFCOUNT) {
|
|
RouteTableEntry *TempRTE;
|
|
|
|
RTE = FindSpecificRTE(Destination, Mask, FirstHop, OutIF, &TempRTE,
|
|
FALSE);
|
|
|
|
if (RTE) {
|
|
ASSERT(RTE->rte_refcnt > 0);
|
|
RTE->rte_refcnt--;
|
|
if (!RTE->rte_refcnt) {
|
|
retval = DelRoute(Destination, Mask, FirstHop, OutIF,
|
|
MatchFlags, &RTE, &pOldBestRTE, &pNewBestRTE);
|
|
} else {
|
|
retval = IP_SUCCESS;
|
|
}
|
|
} else {
|
|
retval = IP_BAD_ROUTE;
|
|
}
|
|
} else {
|
|
|
|
retval = DelRoute(Destination, Mask, FirstHop, OutIF, MatchFlags,
|
|
&RTE, &pOldBestRTE, &pNewBestRTE);
|
|
}
|
|
|
|
if (retval == IP_SUCCESS) {
|
|
if (!((Flags & RT_REFCOUNT) && RTE->rte_refcnt)) {
|
|
|
|
RNO.irno_dest = RTE->rte_dest;
|
|
RNO.irno_mask = RTE->rte_mask;
|
|
RNO.irno_nexthop = GetNextHopForRTE(RTE);
|
|
RNO.irno_proto = RTE->rte_proto;
|
|
RNO.irno_ifindex = OutIF->if_index;
|
|
RNO.irno_metric = RTE->rte_metric;
|
|
RNO.irno_flags = IRNO_FLAG_DELETE;
|
|
|
|
DeleteDone = TRUE;
|
|
CleanupP2MP_RTE(RTE);
|
|
CleanupRTE(RTE);
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
|
|
#if FFP_SUPPORT
|
|
FFPFlushRequired = TRUE;
|
|
#endif
|
|
|
|
if (DeleteDone) {
|
|
if (!(Flags & RT_NO_NOTIFY)) {
|
|
RtChangeNotifyEx(&RNO);
|
|
}
|
|
RtChangeNotify(&RNO);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
//* DeleteRouteWithNoLock - utility routine called by DeleteDest
|
|
//
|
|
// Called to remove a single route for a given destination.
|
|
// It's assumed that this routine is called with the routing table lock held,
|
|
// and that it doesn't release the route-table-lock as part of its operation.
|
|
//
|
|
// Entry: IRE - describes the entry to be deleted
|
|
// DeletedRTE - contains a pointer to the deleted entry on output
|
|
// Flags - selects various semantics for deletion.
|
|
//
|
|
// Returns: IP_SUCCESS if the entry to be deleted was found
|
|
//
|
|
IP_STATUS
|
|
DeleteRouteWithNoLock(IPRouteEntry * IRE, RouteTableEntry **DeletedRTE,
|
|
uint Flags)
|
|
{
|
|
NetTableEntry *OutNTE, *LocalNTE, *TempNTE;
|
|
IPAddr FirstHop, Dest, NextHop;
|
|
uint MTU;
|
|
Interface *OutIF;
|
|
uint Status;
|
|
uint i;
|
|
RouteTableEntry *RTE, *RTE1, *RTE2;
|
|
IPRouteNotifyOutput RNO = {0};
|
|
uint MatchFlags = MATCH_FULL;
|
|
|
|
*DeletedRTE = NULL;
|
|
OutNTE = NULL;
|
|
LocalNTE = NULL;
|
|
|
|
Dest = IRE->ire_dest;
|
|
NextHop = IRE->ire_nexthop;
|
|
|
|
// Make sure that the nexthop is sensible. We don't allow nexthops
|
|
// to be broadcast or invalid or loopback addresses.
|
|
if (IP_LOOPBACK(NextHop) || CLASSD_ADDR(NextHop) || CLASSE_ADDR(NextHop))
|
|
return IP_BAD_REQ;
|
|
|
|
// Also make sure that the destination we're routing to is sensible.
|
|
// Don't allow routes to be added to Class D or E or loopback
|
|
// addresses.
|
|
if (IP_LOOPBACK(Dest) || CLASSD_ADDR(Dest) || CLASSE_ADDR(Dest))
|
|
return IP_BAD_REQ;
|
|
|
|
if (IRE->ire_index == LoopIndex)
|
|
return IP_BAD_REQ;
|
|
|
|
if (IRE->ire_index != INVALID_IF_INDEX) {
|
|
|
|
// First thing to do is to find the outgoing NTE for specified
|
|
// interface, and also make sure that it matches the destination
|
|
// if the destination is one of my addresses.
|
|
|
|
for (i = 0; i < NET_TABLE_SIZE; i++) {
|
|
NetTableEntry *NetTableList = NewNetTableList[i];
|
|
for (TempNTE = NetTableList; TempNTE != NULL;
|
|
TempNTE = TempNTE->nte_next) {
|
|
if ((OutNTE == NULL) && (TempNTE->nte_flags & NTE_VALID) && (IRE->ire_index == TempNTE->nte_if->if_index))
|
|
OutNTE = TempNTE;
|
|
if (!IP_ADDR_EQUAL(NextHop, NULL_IP_ADDR) &&
|
|
IP_ADDR_EQUAL(NextHop, TempNTE->nte_addr) &&
|
|
(TempNTE->nte_flags & NTE_VALID))
|
|
LocalNTE = TempNTE;
|
|
|
|
// Don't let a route be set through a broadcast address.
|
|
if (IsBCastOnNTE(NextHop, TempNTE) != DEST_LOCAL)
|
|
return (IP_STATUS) STATUS_INVALID_PARAMETER;
|
|
|
|
// Don't let a route to a broadcast address be added or deleted.
|
|
if (IsBCastOnNTE(Dest, TempNTE) != DEST_LOCAL)
|
|
return IP_BAD_REQ;
|
|
}
|
|
}
|
|
|
|
// At this point OutNTE points to the outgoing NTE, and LocalNTE
|
|
// points to the NTE for the local address, if this is a direct route.
|
|
// Make sure they point to the same interface, and that the type is
|
|
// reasonable.
|
|
if (OutNTE == NULL)
|
|
return IP_BAD_REQ;
|
|
|
|
if (LocalNTE != NULL) {
|
|
// He's routing straight out a local interface. The interface for
|
|
// the local address must match the interface passed in, and the
|
|
// type must be DIRECT (if we're adding) or INVALID (if we're
|
|
// deleting).
|
|
if (LocalNTE->nte_if->if_index != IRE->ire_index)
|
|
return IP_BAD_REQ;
|
|
|
|
if (IRE->ire_type != IRE_TYPE_DIRECT &&
|
|
IRE->ire_type != IRE_TYPE_INVALID)
|
|
return IP_BAD_REQ;
|
|
OutNTE = LocalNTE;
|
|
}
|
|
// Figure out what the first hop should be. If he's routing straight
|
|
// through a local interface, or the next hop is equal to the
|
|
// destination, then the first hop is IPADDR_LOCAL. Otherwise it's the
|
|
// address of the gateway.
|
|
if ((LocalNTE != NULL) || IP_ADDR_EQUAL(NextHop, NULL_IP_ADDR))
|
|
FirstHop = IPADDR_LOCAL;
|
|
else if (IP_ADDR_EQUAL(Dest, NextHop))
|
|
FirstHop = IPADDR_LOCAL;
|
|
else
|
|
FirstHop = NextHop;
|
|
|
|
MTU = OutNTE->nte_mss;
|
|
OutIF = OutNTE->nte_if;
|
|
|
|
|
|
if (IP_ADDR_EQUAL(NextHop, NULL_IP_ADDR)) {
|
|
|
|
if (!(OutIF->if_flags & IF_FLAGS_P2P)) {
|
|
|
|
return IP_BAD_REQ;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
OutIF = (Interface *) & DummyInterface;
|
|
MTU = DummyInterface.ri_if.if_mtu - sizeof(IPHeader);
|
|
if (IP_ADDR_EQUAL(Dest, NextHop))
|
|
FirstHop = IPADDR_LOCAL;
|
|
else
|
|
FirstHop = NextHop;
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Calling DelRoute On :\n"));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"\tDest = %p\n", Dest));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"\tMask = %p\n", IRE->ire_mask));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"\tIntf = %p\n", OutIF));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"\tNhop = %p\n\n", FirstHop));
|
|
|
|
if (Flags & RT_EXCLUDE_LOCAL) {
|
|
MatchFlags |= MATCH_EXCLUDE_LOCAL;
|
|
}
|
|
|
|
Status = DelRoute(Dest, IRE->ire_mask, FirstHop, OutIF, MatchFlags,
|
|
&RTE, &RTE1, &RTE2);
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Status = %08x\n", Status));
|
|
|
|
if (Status == IP_SUCCESS) {
|
|
|
|
// Queue a route-change notification for the destination-removal.
|
|
//
|
|
// N.B. We are being called with the route-table-lock held;
|
|
// this means we're at DISPATCH_LEVEL, and so the call below
|
|
// to RtChangeNotify will schedule a deferred notification.
|
|
// It definitely *must* not attempt to recursively acquire
|
|
// the route-table-lock, since that would instantly deadlock.
|
|
|
|
RNO.irno_dest = RTE->rte_dest;
|
|
RNO.irno_mask = RTE->rte_mask;
|
|
RNO.irno_nexthop = GetNextHopForRTE(RTE);
|
|
RNO.irno_proto = RTE->rte_proto;
|
|
RNO.irno_ifindex = OutIF->if_index;
|
|
RNO.irno_metric = RTE->rte_metric;
|
|
RNO.irno_flags = IRNO_FLAG_DELETE;
|
|
RtChangeNotify(&RNO);
|
|
|
|
CleanupP2MP_RTE(RTE);
|
|
CleanupRTE(RTE);
|
|
*DeletedRTE = RTE;
|
|
return IP_SUCCESS;
|
|
}
|
|
|
|
return IP_BAD_REQ;
|
|
}
|
|
|
|
//* DeleteDest - delete all routes to a destination
|
|
//
|
|
// Called to remove all routes to a given destination. This results
|
|
// in the entry for the destination itself being removed.
|
|
//
|
|
// Entry: Dest - identifies the destination to be removed
|
|
// Mask - supplies the mask for the destination
|
|
//
|
|
// Returns: IP_SUCCESS if the destination was found
|
|
//
|
|
IP_STATUS
|
|
DeleteDest(IPAddr Dest, IPMask Mask)
|
|
{
|
|
CTELockHandle TableLock;
|
|
RouteTableEntry *RTE, *NextRTE, *DeletedRTE;
|
|
IP_STATUS retval;
|
|
IPRouteEntry IRE;
|
|
BOOLEAN DeleteDone = FALSE;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &TableLock);
|
|
|
|
for (;;) {
|
|
// Begin by locating the first entry for the destination in question.
|
|
// Once we find that, we'll use it to begin a loop in which all the
|
|
// entries for the destination will be deleted.
|
|
|
|
retval = SearchRouteInSTrie(RouteTable->sTrie, Dest, Mask, 0, NULL,
|
|
MATCH_NONE, &RTE);
|
|
|
|
if (retval != IP_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
// Iteratively remove all routes on the destination.
|
|
// Initialize the fields that are common to all the destination's
|
|
// routes, and then iterate over the routes removing each one.
|
|
|
|
IRE.ire_type = IRE_TYPE_INVALID;
|
|
IRE.ire_dest = Dest;
|
|
IRE.ire_mask = Mask;
|
|
|
|
do {
|
|
// Set the fields which are specific to the current entry
|
|
// for the destination (the interface index and nexthop),
|
|
// and pick up the entry *after* this entry (since we're about
|
|
// to delete this entry) so we can continue our enumeration
|
|
// once the current entry is removed.
|
|
|
|
IRE.ire_index = RTE->rte_if->if_index;
|
|
IRE.ire_nexthop = GetNextHopForRTE(RTE);
|
|
|
|
NextRTE = RTE->rte_next;
|
|
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"Deleting RTE @ %p:\n", RTE));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"Next in List = %p:\n", NextRTE));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"Using an IRE @ %p\n", IRE));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"\tDest = %08x\n", IRE.ire_dest));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"\tMask = %08x\n", IRE.ire_mask));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"\tIntf = %08x\n", IRE.ire_index));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"\tNhop = %08x\n\n", IRE.ire_nexthop));
|
|
|
|
// Delete the current entry. The deletion routine
|
|
// takes care of notification, if any.
|
|
|
|
retval = DeleteRouteWithNoLock(&IRE, &DeletedRTE, RT_EXCLUDE_LOCAL);
|
|
if (retval == IP_SUCCESS) {
|
|
DeleteDone = TRUE;
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"Status = %08x, RTE = %p, DeletedRTE = %p\n",
|
|
retval, RTE, DeletedRTE));
|
|
|
|
// Attempt to continue the enumeration by picking up
|
|
// the next entry.
|
|
|
|
if ((retval != IP_SUCCESS) || (RTE == DeletedRTE)) {
|
|
|
|
// Either we are not allowed to delete this route
|
|
// Or we deleted what we were expecting to delete
|
|
|
|
RTE = NextRTE;
|
|
} else {
|
|
|
|
// We deleted an RTE thats further down the list
|
|
// NextRTE might be pointing to this deleted RTE
|
|
// Try to delete again and skip over RTE if cant
|
|
}
|
|
} while (RTE);
|
|
|
|
retval = IP_SUCCESS;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, TableLock);
|
|
|
|
if (DeleteDone) {
|
|
#if FFP_SUPPORT
|
|
FFPFlushRequired = TRUE;
|
|
#endif
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
//* Redirect - Process a redirect request.
|
|
//
|
|
// This is the redirect handler . We treat all redirects as host redirects as
|
|
// per the host requirements RFC. We make a few sanity checks on the new first
|
|
// hop address, and then we look up the current route. If it's not through the
|
|
// source of the redirect, just return.
|
|
// If the current route to the destination is a host route, update the first
|
|
// hop and return.
|
|
// If the route is not a host route, remove any RCE for this route from the
|
|
// RTE, create a host route and place the RCE (if any) on the new RTE.
|
|
//
|
|
// Entry: NTE - Pointer to NetTableEntry for net on which Redirect
|
|
// arrived.
|
|
// RDSrc - IPAddress of source of redirect.
|
|
// Target - IPAddress being redirected.
|
|
// Src - Src IP address of DG that triggered RD.
|
|
// FirstHop - New first hop for Target.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
Redirect(NetTableEntry * NTE, IPAddr RDSrc, IPAddr Target, IPAddr Src,
|
|
IPAddr FirstHop)
|
|
{
|
|
uint MTU;
|
|
RouteTableEntry *RTE;
|
|
CTELockHandle Handle;
|
|
IP_STATUS Status;
|
|
IPRouteNotifyOutput RNO = {0};
|
|
|
|
if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR) ||
|
|
IP_LOOPBACK(FirstHop) ||
|
|
IP_ADDR_EQUAL(FirstHop, RDSrc) ||
|
|
!(NTE->nte_flags & NTE_VALID)) {
|
|
|
|
// Invalid FirstHop
|
|
return;
|
|
}
|
|
|
|
if (GetAddrType(FirstHop) == DEST_LOCAL) {
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"Redirect: Local firsthop %x\n", FirstHop));
|
|
return;
|
|
}
|
|
|
|
// If the redirect is received on a loopback interface, drop it.
|
|
// This can happen in case of NAT, where it sends a packet to an addr in
|
|
// its local pool.
|
|
// These addresses are local but not bound to any interface and IP doesn't
|
|
// know about them
|
|
if (NTE == LoopNTE)
|
|
return;
|
|
|
|
// First make sure that this came from the gateway we're currently using to
|
|
// get to Target, and then lookup up the route to the new first hop. The new
|
|
// firsthop must be directly reachable, and on the same subnetwork or
|
|
// physical interface on which we received the redirect.
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
// Make sure the source of the redirect is the current first hop gateway.
|
|
RTE = LookupRTE(Target, Src, HOST_ROUTE_PRI, FALSE);
|
|
if (RTE == NULL || IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL) ||
|
|
!IP_ADDR_EQUAL(RTE->rte_addr, RDSrc)) {
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return; // A bad redirect.
|
|
|
|
}
|
|
ASSERT(RTE->rte_flags & RTE_IF_VALID);
|
|
|
|
// If the current first hop gateway is a default gateway, see if we have
|
|
// another default gateway at FirstHop that is down. If so, mark him as
|
|
// up and invalidate the RCEs on this guy.
|
|
if (RTE->rte_mask == DEFAULT_MASK && ValidateDefaultGWs(FirstHop) != 0) {
|
|
// Have a default gateway that's been newly activated. Invalidate RCEs
|
|
// on the route, and we're done.
|
|
InvalidateRCEChain(RTE);
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return;
|
|
}
|
|
// We really need to add a host route through FirstHop. Make sure he's
|
|
// a valid first hop.
|
|
RTE = LookupRTE(FirstHop, Src, HOST_ROUTE_PRI, FALSE);
|
|
if (RTE == NULL) {
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return; // Can't get there from here.
|
|
|
|
}
|
|
ASSERT(RTE->rte_flags & RTE_IF_VALID);
|
|
|
|
// Check to make sure the new first hop is directly reachable, and is on the
|
|
// same subnet or physical interface we received the redirect on.
|
|
if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL) || // Not directly reachable
|
|
// or wrong subnet.
|
|
((NTE->nte_addr & NTE->nte_mask) != (FirstHop & NTE->nte_mask))) {
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return;
|
|
}
|
|
if (RTE->rte_link)
|
|
MTU = RTE->rte_link->link_mtu;
|
|
else
|
|
MTU = RTE->rte_mtu;
|
|
|
|
// Now add a host route. AddRoute will do the correct things with shifting
|
|
// RCEs around. We know that FirstHop is on the same subnet as NTE (from
|
|
// the check above), so it's valid to add the route to FirstHop as out
|
|
// going through NTE.
|
|
Status = LockedAddRoute(Target, HOST_MASK,
|
|
IP_ADDR_EQUAL(FirstHop, Target)
|
|
? IPADDR_LOCAL : FirstHop,
|
|
NTE->nte_if, MTU, 1, IRE_PROTO_ICMP, ATYPE_OVERRIDE,
|
|
RTE->rte_context, FALSE, &RNO);
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
|
|
if (Status == IP_SUCCESS && RNO.irno_ifindex) {
|
|
RtChangeNotifyEx(&RNO);
|
|
RtChangeNotify(&RNO);
|
|
}
|
|
|
|
//
|
|
// Bug: #67333: delete the old route thru' RDSrc, now that we have a new one.
|
|
//
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
// "Re-direct: deleting old route thru: %lx, to Target: %lx\n",
|
|
// RDSrc, Target));
|
|
DeleteRoute(Target, HOST_MASK, RDSrc, NTE->nte_if, 0);
|
|
|
|
}
|
|
|
|
//* GetRaisedMTU - Get the next largest MTU in table.
|
|
//
|
|
// A utility function to search the MTU table for a larger value.
|
|
//
|
|
// Input: PrevMTU - MTU we're currently using. We want the next largest one.
|
|
//
|
|
// Returns: New MTU size.
|
|
//
|
|
uint
|
|
GetRaisedMTU(uint PrevMTU)
|
|
{
|
|
uint i;
|
|
|
|
for (i = (sizeof(MTUTable) / sizeof(uint)) - 1; i != 0; i--) {
|
|
if (MTUTable[i] > PrevMTU)
|
|
break;
|
|
}
|
|
|
|
return MTUTable[i];
|
|
}
|
|
|
|
//* GuessNewMTU - Guess a new MTU, giving a DG size too big.
|
|
//
|
|
// A utility function to search the MTU table. As input we take in an MTU
|
|
// size we believe to be too large, and search the table looking for the
|
|
// next smallest one.
|
|
//
|
|
// Input: TooBig - Size that's too big.
|
|
//
|
|
// Returns: New MTU size.
|
|
//
|
|
uint
|
|
GuessNewMTU(uint TooBig)
|
|
{
|
|
uint i;
|
|
|
|
for (i = 0; i < ((sizeof(MTUTable) / sizeof(uint)) - 1); i++)
|
|
if (MTUTable[i] < TooBig)
|
|
break;
|
|
|
|
return MTUTable[i];
|
|
}
|
|
|
|
//* RouteFragNeeded - Handle being told we need to fragment.
|
|
//
|
|
// Called when we receive some external indication that we need to fragment
|
|
// along a particular path. If we're doing MTU discovery we'll try to
|
|
// update the route, if we can. We'll also notify the upper layers about
|
|
// the new MTU.
|
|
//
|
|
// Input: IPH - Pointer to IP Header of datagram needing
|
|
// fragmentation.
|
|
// NewMTU - New MTU to be used (may be 0).
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
RouteFragNeeded(IPHeader UNALIGNED * IPH, ushort NewMTU)
|
|
{
|
|
uint OldMTU;
|
|
CTELockHandle Handle;
|
|
RouteTableEntry *RTE;
|
|
ushort HeaderLength;
|
|
ushort mtu;
|
|
IP_STATUS Status;
|
|
IPRouteNotifyOutput RNO = {0};
|
|
|
|
// If we're not doing PMTU discovery, don't do anything.
|
|
if (!PMTUDiscovery) {
|
|
return;
|
|
}
|
|
|
|
// We're doing PMTU discovery. Before doing any work, make sure this is
|
|
// an acceptable message.
|
|
|
|
if (GetAddrType(IPH->iph_dest) != DEST_REMOTE) {
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
"RouteFragNeeded: non-remote dest %x\n", IPH->iph_dest));
|
|
return;
|
|
}
|
|
|
|
// Correct the given new MTU for the IP header size, which we don't save
|
|
// as we track MTUs.
|
|
if (NewMTU != 0) {
|
|
// Make sure the new MTU we got is at least the minimum valid size.
|
|
NewMTU = MAX(NewMTU, MIN_VALID_MTU);
|
|
NewMTU -= sizeof(IPHeader);
|
|
}
|
|
HeaderLength = (IPH->iph_verlen & (uchar) ~ IP_VER_FLAG) << 2;
|
|
|
|
// Get the current routing information.
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
// Find an RTE for the destination.
|
|
RTE = LookupRTE(IPH->iph_dest, IPH->iph_src, HOST_ROUTE_PRI, FALSE);
|
|
|
|
// If we couldn't find one, give up now.
|
|
if (RTE == NULL) {
|
|
// No RTE. Just bail out now.
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return;
|
|
}
|
|
|
|
if (RTE->rte_link)
|
|
mtu = (ushort) RTE->rte_link->link_mtu;
|
|
else
|
|
mtu = (ushort) RTE->rte_mtu;
|
|
|
|
// If the existing MTU is less than the new
|
|
// MTU, give up now.
|
|
|
|
if ((OldMTU = mtu) < NewMTU) {
|
|
// No RTE, or an invalid new MTU. Just bail out now.
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
return;
|
|
}
|
|
// If the new MTU is zero, figure out what the new MTU should be.
|
|
if (NewMTU == 0) {
|
|
ushort DGLength;
|
|
|
|
// The new MTU is zero. We'll make a best guess what the new
|
|
// MTU should be. We have the RTE for this route already.
|
|
|
|
// Get the length of the datagram that triggered this. Since we'll
|
|
// be comparing it against MTU values that we track without the
|
|
// IP header size included, subtract off that amount.
|
|
DGLength = (ushort) net_short(IPH->iph_length) - sizeof(IPHeader);
|
|
|
|
// We may need to correct this as per RFC 1191 for dealing with
|
|
// old style routers.
|
|
if (DGLength >= OldMTU) {
|
|
// The length of the datagram sent is not less than our
|
|
// current MTU estimate, so we need to back it down (assuming
|
|
// that the sending route has incorrectly added in the header
|
|
// length).
|
|
DGLength = DGLength - (USHORT) HeaderLength;
|
|
|
|
}
|
|
// If it's still larger than our current MTU, use the current
|
|
// MTU. This could happen if the upper layer sends a burst of
|
|
// packets which generate a sequence of ICMP discard messages. The
|
|
// first one we receive will cause us to lower our MTU. We then
|
|
// want to discard subsequent messages to avoid lowering it
|
|
// too much. This could conceivably be a problem if our
|
|
// first adjustment still results in an MTU that's too big,
|
|
// but we should converge adequately fast anyway, and it's
|
|
// better than accidentally underestimating the MTU.
|
|
|
|
if (DGLength > OldMTU)
|
|
NewMTU = (ushort) OldMTU;
|
|
else
|
|
// Move down the table to the next lowest MTU.
|
|
NewMTU = (ushort) GuessNewMTU(DGLength);
|
|
}
|
|
|
|
// We have the new MTU. Now add it to the table as a host route.
|
|
Status = IP_GENERAL_FAILURE;
|
|
if (NewMTU != OldMTU) {
|
|
|
|
// Use ICMP protocol type only when adding a new host route;
|
|
// otherwise, an existing static entry might get overwritten and,
|
|
// later on, timed out as though it were an ICMP route.
|
|
|
|
if (IP_ADDR_EQUAL(RTE->rte_dest,IPH->iph_dest)) {
|
|
|
|
Status = LockedAddRoute(IPH->iph_dest, HOST_MASK, RTE->rte_addr,
|
|
RTE->rte_if, NewMTU, RTE->rte_metric,
|
|
RTE->rte_proto, ATYPE_OVERRIDE,
|
|
RTE->rte_context, FALSE, &RNO);
|
|
} else {
|
|
Status = LockedAddRoute(IPH->iph_dest, HOST_MASK, RTE->rte_addr,
|
|
RTE->rte_if, NewMTU, RTE->rte_metric,
|
|
IRE_PROTO_ICMP, ATYPE_OVERRIDE,
|
|
RTE->rte_context, FALSE, &RNO);
|
|
}
|
|
}
|
|
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
|
|
// We've added the route. Now notify the upper layers of the change.
|
|
ULMTUNotify(IPH->iph_dest, IPH->iph_src, IPH->iph_protocol,
|
|
(void *)((uchar *) IPH + HeaderLength), NewMTU);
|
|
|
|
if (Status == IP_SUCCESS && RNO.irno_ifindex) {
|
|
RtChangeNotifyEx(&RNO);
|
|
RtChangeNotify(&RNO);
|
|
}
|
|
}
|
|
|
|
//** IPRouteTimeout - IP routeing timeout handler.
|
|
//
|
|
// The IP routeing timeout routine, called once a minute. We look at all
|
|
// host routes, and if we raise the MTU on them we do so.
|
|
//
|
|
// Entry: Timer - Timer being fired.
|
|
// Context - Pointer to NTE being time out.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
IPRouteTimeout(CTEEvent * Timer, void *Context)
|
|
{
|
|
uint Now = CTESystemUpTime() / 1000L;
|
|
CTELockHandle Handle;
|
|
RouteTableEntry *RTE, *PrevRTE;
|
|
uint RaiseMTU, Delta;
|
|
Interface *IF;
|
|
IPAddr Dest;
|
|
uint NewMTU;
|
|
NetTableEntry *NTE;
|
|
RouteTableEntry *pOldBestRTE, *pNewBestRTE;
|
|
UINT IsDataLeft, IsValid;
|
|
UCHAR IteratorContext[CONTEXT_SIZE];
|
|
RtChangeList *CurrentRtChangeList = NULL;
|
|
|
|
UNREFERENCED_PARAMETER(Timer);
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
DampCheck();
|
|
|
|
if ((CTEInterlockedIncrementLong(&RouteTimerTicks) * IP_ROUTE_TIMEOUT) ==
|
|
IP_RTABL_TIMEOUT) {
|
|
RouteTimerTicks = 0;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
// First we set up an iterator over all routes
|
|
RtlZeroMemory(IteratorContext, CONTEXT_SIZE);
|
|
|
|
// Do we have any routes at all in the table ?
|
|
IsDataLeft = RTValidateContext(IteratorContext, &IsValid);
|
|
|
|
PrevRTE = NULL;
|
|
|
|
while (IsDataLeft) {
|
|
// Advance context by getting the next route
|
|
IsDataLeft = GetNextRoute(IteratorContext, &RTE);
|
|
|
|
// Do we have to delete the previous route ?
|
|
if (PrevRTE != NULL) {
|
|
IPRouteNotifyOutput RNO = {0};
|
|
RtChangeList *NewRtChange;
|
|
|
|
// Retrieve information about the route for change-notification
|
|
// before proceeding with deletion.
|
|
|
|
RNO.irno_dest = PrevRTE->rte_dest;
|
|
RNO.irno_mask = PrevRTE->rte_mask;
|
|
RNO.irno_nexthop = GetNextHopForRTE(PrevRTE);
|
|
RNO.irno_proto = PrevRTE->rte_proto;
|
|
RNO.irno_ifindex = PrevRTE->rte_if->if_index;
|
|
RNO.irno_metric = PrevRTE->rte_metric;
|
|
RNO.irno_flags = IRNO_FLAG_DELETE;
|
|
|
|
DelRoute(PrevRTE->rte_dest, PrevRTE->rte_mask,
|
|
PrevRTE->rte_addr, PrevRTE->rte_if, MATCH_FULL,
|
|
&PrevRTE, &pOldBestRTE, &pNewBestRTE);
|
|
|
|
CleanupP2MP_RTE(PrevRTE);
|
|
CleanupRTE(PrevRTE);
|
|
|
|
//... so we don't delete same route again
|
|
PrevRTE = NULL;
|
|
|
|
// Allocate, initialize and queue a change-notification entry
|
|
// for the deleted route.
|
|
|
|
NewRtChange = CTEAllocMemNBoot(sizeof(RtChangeList), 'XICT');
|
|
if (NewRtChange != NULL) {
|
|
NewRtChange->rt_next = CurrentRtChangeList;
|
|
NewRtChange->rt_info = RNO;
|
|
CurrentRtChangeList = NewRtChange;
|
|
}
|
|
}
|
|
// Make sure this route is a valid host route
|
|
if (!(RTE->rte_flags & RTE_VALID))
|
|
continue;
|
|
|
|
if (RTE->rte_mask != HOST_MASK)
|
|
continue;
|
|
|
|
// We have valid host route here
|
|
|
|
if (PMTUDiscovery) {
|
|
// Check to see if we can raise the MTU on this guy.
|
|
Delta = Now - RTE->rte_mtuchange;
|
|
|
|
if (RTE->rte_flags & RTE_INCREASE)
|
|
RaiseMTU = (Delta >= MTU_INCREASE_TIME ? 1 : 0);
|
|
else
|
|
RaiseMTU = (Delta >= MTU_DECREASE_TIME ? 1 : 0);
|
|
|
|
if (RaiseMTU) {
|
|
// We need to raise this MTU. Set his change time to
|
|
// Now, so we don't do this again, and figure out
|
|
// what the new MTU should be.
|
|
RTE->rte_mtuchange = Now;
|
|
IF = RTE->rte_if;
|
|
if (RTE->rte_mtu < IF->if_mtu) {
|
|
uint RaisedMTU;
|
|
|
|
RTE->rte_flags |= RTE_INCREASE;
|
|
// This is a candidate for change. Figure out
|
|
// what it should be.
|
|
RaisedMTU = GetRaisedMTU(RTE->rte_mtu);
|
|
NewMTU = MIN(RaisedMTU,
|
|
IF->if_mtu);
|
|
RTE->rte_mtu = NewMTU;
|
|
Dest = RTE->rte_dest;
|
|
|
|
// We have the new MTU. Free the lock, and walk
|
|
// down the NTEs on the I/F. For each NTE,
|
|
// call up to the upper layer and tell him what
|
|
// his new MTU is.
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
NTE = IF->if_nte;
|
|
while (NTE != NULL) {
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
ULMTUNotify(Dest, NTE->nte_addr, 0, NULL,
|
|
MIN(NewMTU, NTE->nte_mss));
|
|
}
|
|
NTE = NTE->nte_ifnext;
|
|
}
|
|
|
|
// We've notified everyone. Get the lock again,
|
|
// and validate context in case something changed
|
|
// after we freed the lock. In case it's invalid,
|
|
// start from first. We've updated the mtuchange
|
|
// time of this RTE, so we won't hit him again.
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
RTValidateContext(IteratorContext, &IsValid);
|
|
|
|
if (!IsValid) {
|
|
RtlZeroMemory(IteratorContext, CONTEXT_SIZE);
|
|
|
|
IsDataLeft = RTValidateContext(IteratorContext, &IsValid);
|
|
|
|
continue;
|
|
}
|
|
// We still have a valid iterator context here
|
|
} else {
|
|
RTE->rte_flags &= ~RTE_INCREASE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this route came in via ICMP, and we have no RCEs on it,
|
|
// and it's at least 10 minutes old, delete it.
|
|
if (RTE->rte_proto == IRE_PROTO_ICMP &&
|
|
RTE->rte_rcelist == NULL &&
|
|
(Now - RTE->rte_valid) > MAX_ICMP_ROUTE_VALID) {
|
|
// He needs to be deleted. Call DelRoute to do this.
|
|
// But after you have updated the context to next RTE
|
|
|
|
// Route for deletion in next iteration
|
|
PrevRTE = RTE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Did we have to delete the previous route ?
|
|
if (PrevRTE != NULL) {
|
|
|
|
IPRouteNotifyOutput RNO = {0};
|
|
RtChangeList *NewRtChange;
|
|
|
|
// Retrieve information about the route for change-notification
|
|
// before proceeding with deletion.
|
|
|
|
RNO.irno_dest = PrevRTE->rte_dest;
|
|
RNO.irno_mask = PrevRTE->rte_mask;
|
|
RNO.irno_nexthop = GetNextHopForRTE(PrevRTE);
|
|
RNO.irno_proto = PrevRTE->rte_proto;
|
|
RNO.irno_ifindex = PrevRTE->rte_if->if_index;
|
|
RNO.irno_metric = PrevRTE->rte_metric;
|
|
RNO.irno_flags = IRNO_FLAG_DELETE;
|
|
|
|
// Delete the route and perform cleanup.
|
|
|
|
DelRoute(PrevRTE->rte_dest, PrevRTE->rte_mask, PrevRTE->rte_addr,
|
|
PrevRTE->rte_if, MATCH_FULL, &PrevRTE, &pOldBestRTE,
|
|
&pNewBestRTE);
|
|
|
|
CleanupP2MP_RTE(PrevRTE);
|
|
CleanupRTE(PrevRTE);
|
|
|
|
// Allocate, initialize and queue a change-notification entry
|
|
// for the deleted route.
|
|
|
|
NewRtChange = CTEAllocMemNBoot(sizeof(RtChangeList), 'DiCT');
|
|
if (NewRtChange != NULL) {
|
|
NewRtChange->rt_next = CurrentRtChangeList;
|
|
NewRtChange->rt_info = RNO;
|
|
CurrentRtChangeList = NewRtChange;
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
}
|
|
#if FFP_SUPPORT
|
|
if (FFPFlushRequired) {
|
|
FFPFlushRequired = FALSE;
|
|
IPFlushFFPCaches();
|
|
}
|
|
#endif
|
|
|
|
if ((CTEInterlockedIncrementLong(&FlushIFTimerTicks) * IP_ROUTE_TIMEOUT) ==
|
|
FLUSH_IFLIST_TIMEOUT) {
|
|
Interface *TmpIF;
|
|
RouteCacheEntry *RCE, *PrevRCE;
|
|
|
|
FlushIFTimerTicks = 0;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
|
|
// check whether FreeIFList is non empty
|
|
if (FrontFreeList) {
|
|
ASSERT(*(int *)&TotalFreeInterfaces > 0);
|
|
// free the first interface in the list
|
|
TmpIF = FrontFreeList;
|
|
FrontFreeList = FrontFreeList->if_next;
|
|
CTEFreeMem(TmpIF);
|
|
TotalFreeInterfaces--;
|
|
|
|
// check whether the list became empty
|
|
if (FrontFreeList == NULL) {
|
|
RearFreeList = NULL;
|
|
ASSERT(TotalFreeInterfaces == 0);
|
|
}
|
|
}
|
|
// use the same timer to scan the RCEFreeList
|
|
|
|
PrevRCE = STRUCT_OF(RouteCacheEntry, &RCEFreeList, rce_next);
|
|
RCE = RCEFreeList;
|
|
|
|
while (RCE) {
|
|
if (RCE->rce_usecnt == 0) {
|
|
RouteCacheEntry *nextRCE;
|
|
// time to free this RCE
|
|
// remove it from the list
|
|
PrevRCE->rce_next = RCE->rce_next;
|
|
if (RCE->rce_flags & RCE_REFERENCED) {
|
|
// IF is ref'd so it better be in the IFList
|
|
LockedDerefIF((Interface *) RCE->rce_rte);
|
|
}
|
|
nextRCE = RCE->rce_next;
|
|
CTEFreeMem(RCE);
|
|
RCE = nextRCE;
|
|
} else {
|
|
PrevRCE = RCE;
|
|
RCE = RCE->rce_next;
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
}
|
|
|
|
// Call RtChangeNotify for each of the entries in the change-notification
|
|
// list that we've built up so far. In the process, free each entry.
|
|
|
|
if (CurrentRtChangeList) {
|
|
RtChangeList *TmpRtChangeList;
|
|
|
|
do {
|
|
TmpRtChangeList = CurrentRtChangeList->rt_next;
|
|
RtChangeNotify(&CurrentRtChangeList->rt_info);
|
|
CTEFreeMem(CurrentRtChangeList);
|
|
CurrentRtChangeList = TmpRtChangeList;
|
|
} while(CurrentRtChangeList);
|
|
}
|
|
|
|
// If the driver is unloading, dont restart the timer
|
|
|
|
if (fRouteTimerStopping) {
|
|
CTESignal(&TcpipUnloadBlock, NDIS_STATUS_SUCCESS);
|
|
} else {
|
|
CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL);
|
|
}
|
|
}
|
|
|
|
//* FreeFWPacket - Free a fowarding packet to its pool.
|
|
//
|
|
// Input: Packet - Packet to be freed.
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
FreeFWPacket(PNDIS_PACKET Packet)
|
|
{
|
|
FWContext *FWC = (FWContext *)Packet->ProtocolReserved;
|
|
|
|
ASSERT(FWC->fc_pc.pc_common.pc_IpsecCtx == NULL);
|
|
|
|
// Return any buffers to their respective pools.
|
|
//
|
|
if (FWC->fc_buffhead) {
|
|
PNDIS_BUFFER Head, Mdl;
|
|
Head = FWC->fc_buffhead;
|
|
do {
|
|
Mdl = Head;
|
|
Head = Head->Next;
|
|
MdpFree(Mdl);
|
|
} while (Head);
|
|
FWC->fc_buffhead = NULL;
|
|
}
|
|
|
|
if (FWC->fc_options) {
|
|
CTEFreeMem(FWC->fc_options);
|
|
FWC->fc_options = NULL;
|
|
FWC->fc_optlength = 0;
|
|
FWC->fc_pc.pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS;
|
|
}
|
|
|
|
if (FWC->fc_iflink) {
|
|
DerefLink(FWC->fc_iflink);
|
|
FWC->fc_iflink = NULL;
|
|
}
|
|
|
|
if (FWC->fc_if) {
|
|
DerefIF(FWC->fc_if);
|
|
FWC->fc_if = NULL;
|
|
}
|
|
|
|
NdisReinitializePacket(Packet);
|
|
#if MCAST_BUG_TRACKING
|
|
FWC->fc_pc.pc_common.pc_owner = 0;
|
|
#endif
|
|
|
|
FwPacketFree(Packet);
|
|
}
|
|
|
|
//* FWSendComplete - Complete the transmission of a forwarded packet.
|
|
//
|
|
// This is called when the send of a forwarded packet is done. We'll free the
|
|
// resources and get the next send going, if there is one. If there isn't,
|
|
// we'll decrement the pending count.
|
|
//
|
|
// Input: Packet - Packet being completed.
|
|
// Buffer - Pointer to buffer chain being completed.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
FWSendComplete(void *SendContext, PNDIS_BUFFER Buffer, IP_STATUS SendStatus)
|
|
{
|
|
PNDIS_PACKET Packet = (PNDIS_PACKET) SendContext;
|
|
FWContext *FWC = (FWContext *) Packet->ProtocolReserved;
|
|
RouteSendQ *RSQ;
|
|
CTELockHandle Handle;
|
|
FWQ *NewFWQ;
|
|
PNDIS_PACKET NewPacket;
|
|
|
|
UNREFERENCED_PARAMETER(SendStatus);
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
FWC->fc_MacHdrSize = SendStatus;
|
|
#endif
|
|
|
|
if (Buffer && FWC->fc_bufown) {
|
|
|
|
//Undo the offset manipulation
|
|
//which was done in super fast path
|
|
|
|
int MacHeaderSize = FWC->fc_MacHdrSize;
|
|
PNDIS_PACKET RtnPacket = FWC->fc_bufown;
|
|
|
|
NdisAdjustBuffer(
|
|
Buffer,
|
|
(PCHAR) NdisBufferVirtualAddress(Buffer) - MacHeaderSize,
|
|
NdisBufferLength(Buffer) + MacHeaderSize);
|
|
|
|
Packet->Private.Head = NULL;
|
|
Packet->Private.Tail = NULL;
|
|
|
|
NdisReturnPackets(&RtnPacket, 1);
|
|
|
|
FWC->fc_bufown = NULL;
|
|
#if MCAST_BUG_TRACKING
|
|
FWC->fc_sos = __LINE__;
|
|
#endif
|
|
|
|
FreeFWPacket(Packet);
|
|
|
|
return;
|
|
|
|
}
|
|
if (!IS_BCAST_DEST(FWC->fc_dtype))
|
|
RSQ = &((RouteInterface *) FWC->fc_if)->ri_q;
|
|
else
|
|
RSQ = BCastRSQ;
|
|
|
|
if (IS_MCAST_DEST(FWC->fc_dtype)) {
|
|
RSQ = NULL;
|
|
}
|
|
#if MCAST_BUG_TRACKING
|
|
FWC->fc_sos = __LINE__;
|
|
#endif
|
|
|
|
FreeFWPacket(Packet);
|
|
|
|
if (RSQ == NULL) {
|
|
return;
|
|
}
|
|
CTEGetLock(&RSQ->rsq_lock, &Handle);
|
|
ASSERT(RSQ->rsq_pending <= RSQ->rsq_maxpending);
|
|
|
|
RSQ->rsq_pending--;
|
|
|
|
ASSERT(*(int *)&RSQ->rsq_pending >= 0);
|
|
|
|
if (RSQ->rsq_qlength != 0) { // Have more to send.
|
|
|
|
// Make sure we're not already running through this. If we are, quit.
|
|
if (!RSQ->rsq_running) {
|
|
|
|
// We could schedule this off for an event, but under NT that
|
|
// could me a context switch for every completing packet in the
|
|
// normal case. For now, just do it in a loop guarded with
|
|
// rsq_running.
|
|
RSQ->rsq_running = TRUE;
|
|
|
|
// Loop while we haven't hit our send limit and we still have
|
|
// stuff to send.
|
|
while (RSQ->rsq_pending < RSQ->rsq_maxpending &&
|
|
RSQ->rsq_qlength != 0) {
|
|
|
|
ASSERT(RSQ->rsq_qh.fq_next != &RSQ->rsq_qh);
|
|
|
|
// Pull one off the queue, and update qlength.
|
|
NewFWQ = RSQ->rsq_qh.fq_next;
|
|
RSQ->rsq_qh.fq_next = NewFWQ->fq_next;
|
|
NewFWQ->fq_next->fq_prev = NewFWQ->fq_prev;
|
|
RSQ->rsq_qlength--;
|
|
|
|
// Update pending before we send.
|
|
RSQ->rsq_pending++;
|
|
CTEFreeLock(&RSQ->rsq_lock, Handle);
|
|
NewPacket = PACKET_FROM_FWQ(NewFWQ);
|
|
TransmitFWPacket(NewPacket,
|
|
((FWContext *) NewPacket->ProtocolReserved)->fc_datalength);
|
|
CTEGetLock(&RSQ->rsq_lock, &Handle);
|
|
}
|
|
|
|
RSQ->rsq_running = FALSE;
|
|
}
|
|
}
|
|
CTEFreeLock(&RSQ->rsq_lock, Handle);
|
|
}
|
|
|
|
//* TransmitFWPacket - Transmit a forwarded packet on a link.
|
|
//
|
|
// Called when we know we can send a packet. We fix up the header, and send it.
|
|
//
|
|
// Input: Packet - Packet to be sent.
|
|
// DataLength - Length of data.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
TransmitFWPacket(PNDIS_PACKET Packet, uint DataLength)
|
|
{
|
|
FWContext *FC = (FWContext *) Packet->ProtocolReserved;
|
|
PNDIS_BUFFER HBuffer, Buffer;
|
|
IP_STATUS Status;
|
|
ULONG ipsecByteCount = 0;
|
|
ULONG ipsecMTU;
|
|
ULONG ipsecFlags;
|
|
IPHeader *IPH;
|
|
ULONG len;
|
|
IPAddr SrcAddr = 0;
|
|
PNDIS_BUFFER OptBuffer = NULL;
|
|
PNDIS_BUFFER newBuf = NULL;
|
|
IPHeader *pSaveIPH = NULL;
|
|
UCHAR saveIPH[MAX_IP_HDR_SIZE + ICMP_HEADER_SIZE];
|
|
void *ArpCtxt = NULL;
|
|
|
|
//
|
|
// Fix up the packet. Remove the existing buffer chain, and put our
|
|
// header on the front.
|
|
//
|
|
|
|
|
|
Buffer = Packet->Private.Head;
|
|
HBuffer = FC->fc_hndisbuff;
|
|
Packet->Private.Head = HBuffer;
|
|
Packet->Private.Tail = HBuffer;
|
|
NDIS_BUFFER_LINKAGE(HBuffer) = (PNDIS_BUFFER) NULL;
|
|
Packet->Private.TotalLength = sizeof(IPHeader);
|
|
Packet->Private.Count = 1;
|
|
|
|
|
|
TcpipQueryBuffer(HBuffer, (PVOID *) &IPH, (PUINT)&len, NormalPagePriority);
|
|
|
|
if (IPH == NULL) {
|
|
#if MCAST_BUG_TRACKING
|
|
FC->fc_mtu = __LINE__;
|
|
#endif
|
|
FWSendComplete(Packet, Buffer, IP_SUCCESS);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return;
|
|
}
|
|
|
|
Packet->Private.PhysicalCount =
|
|
ADDRESS_AND_SIZE_TO_SPAN_PAGES(IPH,
|
|
sizeof(IPHeader));
|
|
|
|
if (IPSecHandlerPtr) {
|
|
//
|
|
// See if IPSEC is enabled, see if it needs to do anything with this
|
|
// packet - we need to construct the full IP header in the first MDL
|
|
// before we call out to IPSEC.
|
|
//
|
|
IPSEC_ACTION Action;
|
|
ulong csum;
|
|
PUCHAR pTpt;
|
|
ULONG tptLen;
|
|
|
|
pSaveIPH = (IPHeader *) saveIPH;
|
|
*pSaveIPH = *IPH;
|
|
|
|
csum = xsum(IPH, sizeof(IPHeader));
|
|
|
|
//
|
|
// Link the header buffer to the options buffer before we indicate
|
|
// to IPSEC
|
|
//
|
|
|
|
if (FC->fc_options) {
|
|
|
|
//
|
|
// Allocate the MDL for options too
|
|
//
|
|
|
|
NdisAllocateBuffer((PNDIS_STATUS) &Status,
|
|
&OptBuffer,
|
|
BufferPool,
|
|
FC->fc_options,
|
|
(uint) FC->fc_optlength);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Couldn't get the needed option buffer.
|
|
//
|
|
#if MCAST_BUG_TRACKING
|
|
FC->fc_mtu = __LINE__;
|
|
#endif
|
|
FWSendComplete(Packet, Buffer, IP_SUCCESS);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return;
|
|
}
|
|
NDIS_BUFFER_LINKAGE(HBuffer) = OptBuffer;
|
|
NDIS_BUFFER_LINKAGE(OptBuffer) = Buffer;
|
|
|
|
//
|
|
// update the xsum in the IP header
|
|
//
|
|
|
|
FC->fc_pc.pc_common.pc_flags |= PACKET_FLAG_OPTIONS;
|
|
NdisChainBufferAtBack(Packet, OptBuffer);
|
|
csum += xsum(FC->fc_options, (uint) FC->fc_optlength);
|
|
csum = (csum >> 16) + (csum & 0xffff);
|
|
csum += (csum >> 16);
|
|
|
|
} else {
|
|
|
|
NDIS_BUFFER_LINKAGE(HBuffer) = Buffer;
|
|
}
|
|
|
|
//
|
|
// Prepare ourselves for sending an ICMP dont frag in case
|
|
// IPSEC bloats beyond the MTU on this interface.
|
|
//
|
|
// SendICMPErr expects the next transport header in the same
|
|
// contiguous buffer as the IPHeader, with or without options.
|
|
// We need to ensure that this is satisfied if in fact we need to
|
|
// fragment on account of IPSEC. So, setup the buffer right here.
|
|
//
|
|
|
|
//
|
|
// If this is a zero-payload packet (i.e. just a header), then Buffer
|
|
// is NULL and there is nothing for IPSEC to bloat. We only have to
|
|
// deal with the don't fragment flag if there is a Buffer.
|
|
//
|
|
if (Buffer && (pSaveIPH->iph_offset & IP_DF_FLAG)) {
|
|
|
|
TcpipQueryBuffer(Buffer, &pTpt, (PUINT) &tptLen,
|
|
NormalPagePriority);
|
|
if (pTpt == NULL) {
|
|
#if MCAST_BUG_TRACKING
|
|
FC->fc_mtu = __LINE__;
|
|
#endif
|
|
FWSendComplete(Packet, Buffer, IP_SUCCESS);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return;
|
|
}
|
|
|
|
if (FC->fc_options) {
|
|
RtlCopyMemory(((PUCHAR) (pSaveIPH + 1)),
|
|
FC->fc_options, FC->fc_optlength);
|
|
}
|
|
|
|
RtlCopyMemory(((PUCHAR) (pSaveIPH + 1)) + FC->fc_optlength,
|
|
pTpt,
|
|
MIN(tptLen,ICMP_HEADER_SIZE));
|
|
|
|
|
|
|
|
}
|
|
IPH->iph_xsum = ~(ushort) csum;
|
|
|
|
SrcAddr = FC->fc_if->if_nte->nte_addr;
|
|
|
|
ipsecMTU = FC->fc_mtu;
|
|
if ((DataLength + (uint) FC->fc_optlength) < FC->fc_mtu) {
|
|
ipsecByteCount = FC->fc_mtu - (DataLength + (uint) FC->fc_optlength);
|
|
}
|
|
ipsecFlags = IPSEC_FLAG_FORWARD;
|
|
Action = (*IPSecHandlerPtr) ((PUCHAR) IPH,
|
|
(PVOID) HBuffer,
|
|
FC->fc_if,
|
|
Packet,
|
|
&ipsecByteCount,
|
|
&ipsecMTU,
|
|
(PVOID) & newBuf,
|
|
&ipsecFlags,
|
|
FC->fc_dtype);
|
|
|
|
if (Action != eFORWARD) {
|
|
#if MCAST_BUG_TRACKING
|
|
FC->fc_mtu = __LINE__;
|
|
#endif
|
|
FWSendComplete(Packet, Buffer, IP_SUCCESS);
|
|
|
|
IPSInfo.ipsi_outdiscards++;
|
|
|
|
//
|
|
// We can get MTU redeuced also when forwarding because in the nested
|
|
// tunneling configuration, the tunnel that starts from this machine
|
|
// can get a ICMP PMTU packet. We can't reduce the MTU on the interface
|
|
// but we can send back to the sender (which can be a router with yet
|
|
// another tunnel for this packet) a PMTU packet asking him to reduce his
|
|
// MTU even further. If the sender is an end-station, this PMTU info
|
|
// will eventually propogate back to TCP stack. If it is a router, the
|
|
// same logic used here will be applied. The MTU info will thus be
|
|
// relayed all the way back to the original sender (TCP stack).
|
|
// Of course the more common case is that a packet with the added IPSec
|
|
// header exceeds the link MTU. No matter what is the case, we send the
|
|
// new MTU information back to the sender.
|
|
//
|
|
if (ipsecMTU) {
|
|
SendICMPIPSecErr(SrcAddr,
|
|
pSaveIPH,
|
|
ICMP_DEST_UNREACH,
|
|
FRAG_NEEDED,
|
|
net_long((ulong) (ipsecMTU + sizeof(IPHeader))));
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"TransmitFWPacket: Sent ICMP frag_needed to %lx, from src: %lx\n", pSaveIPH->iph_src, SrcAddr));
|
|
}
|
|
return;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Use the new buffer chain - IPSEC will restore the old one
|
|
// on send complete
|
|
//
|
|
|
|
if (newBuf) {
|
|
|
|
NdisReinitializePacket(Packet);
|
|
NdisChainBufferAtBack(Packet, newBuf);
|
|
}
|
|
DataLength += ipsecByteCount;
|
|
}
|
|
}
|
|
//
|
|
// Figure out how to send it. If it's not a broadcast we'll either
|
|
// send it or have it fragmented. If it is a broadcast we'll let our
|
|
// send broadcast routine handle it.
|
|
//
|
|
|
|
if (FC->fc_dtype != DEST_BCAST) {
|
|
|
|
if ((DataLength + (uint) FC->fc_optlength) <= FC->fc_mtu) {
|
|
|
|
if (FC->fc_iflink) {
|
|
|
|
ASSERT(FC->fc_if->if_flags & IF_FLAGS_P2MP);
|
|
ArpCtxt = FC->fc_iflink->link_arpctxt;
|
|
|
|
}
|
|
//
|
|
// In case of synchronous completion though
|
|
// FreeIPPacket is called, which will not
|
|
// free the FW packet.
|
|
//
|
|
Status = SendIPPacket(FC->fc_if,
|
|
FC->fc_nexthop,
|
|
Packet,
|
|
Buffer,
|
|
FC->fc_hbuff,
|
|
FC->fc_options,
|
|
(uint) FC->fc_optlength,
|
|
(BOOLEAN) (IPSecHandlerPtr != NULL),
|
|
ArpCtxt,
|
|
FALSE);
|
|
} else {
|
|
|
|
//
|
|
// Need to fragment this.
|
|
//
|
|
|
|
BufferReference *BR = CTEAllocMemN(sizeof(BufferReference), 'GiCT');
|
|
|
|
if (BR == (BufferReference *) NULL) {
|
|
|
|
//
|
|
// Couldn't get a BufferReference
|
|
//
|
|
#if MCAST_BUG_TRACKING
|
|
FC->fc_mtu = __LINE__;
|
|
#endif
|
|
if (!IPSecHandlerPtr) {
|
|
FWSendComplete(Packet, Buffer, IP_SUCCESS);
|
|
return;
|
|
}
|
|
|
|
|
|
} else {
|
|
BR->br_buffer = Buffer;
|
|
BR->br_refcount = 0;
|
|
CTEInitLock(&BR->br_lock);
|
|
FC->fc_pc.pc_br = BR;
|
|
BR->br_userbuffer = 0;
|
|
|
|
}
|
|
|
|
if (IPSecHandlerPtr) {
|
|
|
|
Buffer = NDIS_BUFFER_LINKAGE(HBuffer);
|
|
|
|
//
|
|
// This is to ensure that options are freed appropriately.
|
|
// In the fragment code, the first fragment inherits the
|
|
// options of the entire packet; but these packets have
|
|
// no IPSEC context, hence cannot be freed appropriately.
|
|
// So, we allocate temporary options here and use these
|
|
// to represent the real options. These are freed when the
|
|
// first fragment is freed and the real options are freed here.
|
|
//
|
|
|
|
if (FC->fc_options) {
|
|
|
|
|
|
if (newBuf) {
|
|
|
|
//
|
|
// if a new buffer chain was returned above by IPSEC,
|
|
// then it is most prob. a tunnel => options were
|
|
// copied, hence get rid of ours.
|
|
//
|
|
|
|
NdisFreeBuffer(OptBuffer);
|
|
CTEFreeMem(FC->fc_options);
|
|
FC->fc_options = NULL;
|
|
FC->fc_optlength = 0;
|
|
|
|
} else {
|
|
|
|
Buffer = NDIS_BUFFER_LINKAGE(OptBuffer);
|
|
NdisFreeBuffer(OptBuffer);
|
|
|
|
}
|
|
|
|
FC->fc_pc.pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS;
|
|
}
|
|
NDIS_BUFFER_LINKAGE(HBuffer) = NULL;
|
|
NdisReinitializePacket(Packet);
|
|
NdisChainBufferAtBack(Packet, HBuffer);
|
|
IPH->iph_xsum = 0;
|
|
|
|
//
|
|
// If the DF flag is set, make sure the packet doesn't need
|
|
// fragmentation. If this is the case, send an ICMP error
|
|
// now while we still have the original IP header. The ICMP
|
|
// message includes the MTU so the source host can perform
|
|
// Path MTU discovery.
|
|
//
|
|
// IPSEC headers might have caused this to happen.
|
|
// Send an ICMP to the source so he can adjust his MTU.
|
|
//
|
|
|
|
if (IPH->iph_offset & IP_DF_FLAG) {
|
|
|
|
IPSInfo.ipsi_fragfails++;
|
|
|
|
SendICMPIPSecErr(SrcAddr,
|
|
pSaveIPH,
|
|
ICMP_DEST_UNREACH,
|
|
FRAG_NEEDED,
|
|
net_long((ulong) (FC->fc_mtu - ipsecByteCount + sizeof(IPHeader))));
|
|
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"TransmitFWPacket: Sent ICMP frag_needed to %lx, from src: %lx\n", pSaveIPH->iph_src, SrcAddr));
|
|
|
|
// FreeIPpacket will do header fix up if
|
|
// original header chain was modified by ipsec/firewall/hdrincl
|
|
|
|
Status = IP_PACKET_TOO_BIG;
|
|
FreeIPPacket(Packet, TRUE, Status);
|
|
|
|
// Don't want to fall through and complete packet after
|
|
// we have freed it.
|
|
return;
|
|
|
|
} else {
|
|
|
|
if (BR == NULL) {
|
|
FreeIPPacket(Packet, TRUE, IP_NO_RESOURCES);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// DF bit is not set, ok to fragment
|
|
//
|
|
|
|
if (FC->fc_iflink) {
|
|
|
|
ASSERT(FC->fc_if->if_flags & IF_FLAGS_P2MP);
|
|
ArpCtxt = FC->fc_iflink->link_arpctxt;
|
|
|
|
}
|
|
Status = IPFragment(FC->fc_if,
|
|
FC->fc_mtu - ipsecByteCount,
|
|
FC->fc_nexthop,
|
|
Packet,
|
|
FC->fc_hbuff,
|
|
Buffer,
|
|
DataLength,
|
|
FC->fc_options,
|
|
(uint) FC->fc_optlength,
|
|
(int *)NULL,
|
|
FALSE,
|
|
ArpCtxt);
|
|
|
|
//
|
|
// Fragmentation needed with the DF flag set should have
|
|
// been handled in IPForward. We don't have the original
|
|
// header any longer, so silently drop the packet.
|
|
//
|
|
|
|
ASSERT(Status != IP_PACKET_TOO_BIG);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// No IPSec handler. No need to check for DF bit here
|
|
// because unlike in the IPSec case, we are not messing
|
|
// with the MTUs so the DF check done in IPForwardPkt is
|
|
// valid
|
|
//
|
|
|
|
if (FC->fc_iflink) {
|
|
ASSERT(FC->fc_if->if_flags & IF_FLAGS_P2MP);
|
|
ArpCtxt = FC->fc_iflink->link_arpctxt;
|
|
}
|
|
Status = IPFragment(FC->fc_if,
|
|
FC->fc_mtu - ipsecByteCount,
|
|
FC->fc_nexthop,
|
|
Packet,
|
|
FC->fc_hbuff,
|
|
Buffer,
|
|
DataLength,
|
|
FC->fc_options,
|
|
(uint) FC->fc_optlength,
|
|
(int *)NULL,
|
|
FALSE,
|
|
ArpCtxt);
|
|
//
|
|
// Fragmentation needed with the DF flag set should have been
|
|
// handled in IPForward. We don't have the original header
|
|
// any longer, so silently drop the packet.
|
|
//
|
|
|
|
ASSERT(Status != IP_PACKET_TOO_BIG);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Dest type is bcast
|
|
//
|
|
|
|
Status = SendIPBCast(FC->fc_srcnte,
|
|
FC->fc_nexthop,
|
|
Packet,
|
|
FC->fc_hbuff,
|
|
Buffer,
|
|
DataLength,
|
|
FC->fc_options,
|
|
(uint) FC->fc_optlength,
|
|
FC->fc_sos,
|
|
&FC->fc_index);
|
|
|
|
}
|
|
|
|
if (Status != IP_PENDING) {
|
|
#if MCAST_BUG_TRACKING
|
|
FC->fc_mtu = __LINE__;
|
|
#endif
|
|
FWSendComplete(Packet, Buffer, IP_SUCCESS);
|
|
}
|
|
}
|
|
|
|
//* SendFWPacket - Send a packet that needs to be forwarded.
|
|
//
|
|
// This routine is invoked when we actually get around to sending a packet.
|
|
// We look and see if we can give another queued send to the outgoing link,
|
|
// and if so we send on that link. Otherwise we put it on the outgoing queue
|
|
// and remove it later.
|
|
//
|
|
// Input: SrcNTE - Source NTE of packet.
|
|
// Packet - Packet to be send, containg all needed context info.
|
|
// Status - Status of transfer data.
|
|
// DataLength - Length in bytes of data to be send.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
SendFWPacket(PNDIS_PACKET Packet, NDIS_STATUS Status, uint DataLength)
|
|
{
|
|
|
|
FWContext *FC = (FWContext *) Packet->ProtocolReserved;
|
|
Interface *IF = FC->fc_if;
|
|
RouteSendQ *RSQ;
|
|
CTELockHandle Handle;
|
|
|
|
if (Status == NDIS_STATUS_SUCCESS) {
|
|
// Figure out which logical queue it belongs on, and if we don't already
|
|
// have too many things going there, send it. If we can't send it now we'll
|
|
// queue it for later.
|
|
if (IS_BCAST_DEST(FC->fc_dtype))
|
|
RSQ = BCastRSQ;
|
|
else
|
|
RSQ = &((RouteInterface *) IF)->ri_q;
|
|
|
|
CTEGetLock(&RSQ->rsq_lock, &Handle);
|
|
|
|
if ((RSQ->rsq_pending < RSQ->rsq_maxpending) && (RSQ->rsq_qlength == 0)) {
|
|
// We can send on this interface.
|
|
RSQ->rsq_pending++;
|
|
CTEFreeLock(&RSQ->rsq_lock, Handle);
|
|
|
|
TransmitFWPacket(Packet, DataLength);
|
|
|
|
} else { // Need to queue this packet for later.
|
|
|
|
FC->fc_datalength = DataLength;
|
|
FC->fc_q.fq_next = &RSQ->rsq_qh;
|
|
FC->fc_q.fq_prev = RSQ->rsq_qh.fq_prev;
|
|
RSQ->rsq_qh.fq_prev->fq_next = &FC->fc_q;
|
|
RSQ->rsq_qh.fq_prev = &FC->fc_q;
|
|
RSQ->rsq_qlength++;
|
|
CTEFreeLock(&RSQ->rsq_lock, Handle);
|
|
}
|
|
|
|
} else {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
#if MCAST_BUG_TRACKING
|
|
FC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(Packet);
|
|
}
|
|
|
|
}
|
|
|
|
//* GetFWBufferChain - Get a buffer chain from our buffer pools
|
|
// sufficiently long enough to be able to copy DataLength bytes into it.
|
|
//
|
|
// Input: DataLength - Length in bytes that the buffer chain must be able
|
|
// to describe.
|
|
// Packet - Forwarding packet to link the buffer chain into.
|
|
// TailPointer - Returned pointer to the tail of the buffer chain.
|
|
//
|
|
// Returns: Pointer to the head of the buffer chain on success, NULL
|
|
// on failure.
|
|
//
|
|
PNDIS_BUFFER
|
|
GetFWBufferChain(uint DataLength, PNDIS_PACKET Packet,
|
|
PNDIS_BUFFER *TailPointer)
|
|
{
|
|
KIRQL OldIrql;
|
|
PNDIS_BUFFER Head, Tail, Mdl;
|
|
HANDLE PoolHandle;
|
|
PVOID Buffer;
|
|
uint Remaining, Length;
|
|
|
|
// Raise to dispatch level to make multiple calls to MdpAllocate
|
|
// more efficient. This is no less efficient in the single call case
|
|
// either.
|
|
//
|
|
#if !MILLEN
|
|
OldIrql = KeRaiseIrqlToDpcLevel();
|
|
#endif
|
|
|
|
// Loop allocating buffers until we have enough to describe DataLength.
|
|
//
|
|
Head = NULL;
|
|
Tail = NULL;
|
|
|
|
for (Remaining = DataLength; Remaining != 0; Remaining -= Length) {
|
|
|
|
// Figure out which buffer pool to use based on the length
|
|
// of data remaining. Use "large" buffers unless the remaining
|
|
// data will fit in a "small" buffer.
|
|
//
|
|
if (Remaining >= BUFSIZE_LARGE_POOL) {
|
|
PoolHandle = IpForwardLargePool;
|
|
Length = BUFSIZE_LARGE_POOL;
|
|
} else if (Remaining > BUFSIZE_SMALL_POOL) {
|
|
PoolHandle = IpForwardLargePool;
|
|
Length = Remaining;
|
|
} else {
|
|
PoolHandle = IpForwardSmallPool;
|
|
Length = Remaining;
|
|
}
|
|
|
|
// Allocate a buffer from the chosen pool and link it at the tail.
|
|
//
|
|
Mdl = MdpAllocateAtDpcLevel(PoolHandle, &Buffer);
|
|
if (Mdl) {
|
|
|
|
// Expect MdpAllocate to initialize Mdl->Next.
|
|
//
|
|
ASSERT(!Mdl->Next);
|
|
|
|
NdisAdjustBufferLength(Mdl, Length);
|
|
|
|
if (!Head) {
|
|
Head = Mdl;
|
|
} else {
|
|
Tail->Next = Mdl;
|
|
}
|
|
|
|
Tail = Mdl;
|
|
|
|
} else {
|
|
// Free what we allocated so far and quit the loop.
|
|
//
|
|
while (Head) {
|
|
Mdl = Head;
|
|
Head = Head->Next;
|
|
MdpFree(Mdl);
|
|
}
|
|
|
|
// Need to leave the loop with Head == NULL in the error
|
|
// case for the remaining logic to work correctly.
|
|
//
|
|
ASSERT(!Head);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
#if !MILLEN
|
|
KeLowerIrql(OldIrql);
|
|
#endif
|
|
|
|
// If we've succeeded, put the buffer chain in the packet and
|
|
// adjust our forwarding context.
|
|
//
|
|
if (Head) {
|
|
FWContext *FWC = (FWContext *)Packet->ProtocolReserved;
|
|
|
|
ASSERT(Tail);
|
|
|
|
NdisChainBufferAtFront(Packet, Head);
|
|
FWC->fc_buffhead = Head;
|
|
FWC->fc_bufftail = Tail;
|
|
*TailPointer = Tail;
|
|
}
|
|
|
|
return Head;
|
|
}
|
|
|
|
//* AllocateCopyBuffers - Get a buffer chain from our buffer pools
|
|
// sufficiently long enough to be able to copy DataLength bytes into it.
|
|
//
|
|
// Input: Packet - Forwarding packet to link the buffer chain into.
|
|
// DataLength - Length in bytes that the buffer chain must be able
|
|
// to describe.
|
|
// Head - Returned pointer to the head of the buffer chain.
|
|
// CountBuffers - Returned count of buffers in the chain.
|
|
//
|
|
// Returns: NDIS_STATUS_SUCCESS or NDIS_STATUS_RESOURCES
|
|
//
|
|
NDIS_STATUS
|
|
AllocateCopyBuffers(PNDIS_PACKET Packet, uint DataLength, PNDIS_BUFFER *Head,
|
|
uint *CountBuffers)
|
|
{
|
|
PNDIS_BUFFER Tail, Mdl;
|
|
uint Count = 0;
|
|
|
|
*Head = GetFWBufferChain(DataLength, Packet, &Tail);
|
|
if (*Head) {
|
|
for (Count = 1, Mdl = *Head; Mdl != Tail; Mdl = Mdl->Next, Count++);
|
|
|
|
*CountBuffers = Count;
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
//* GetFWBuffer - Get a list of buffers for forwarding.
|
|
//
|
|
// This routine gets a list of buffers for forwarding, and puts the data into
|
|
// it. This may involve calling TransferData, or we may be able to copy
|
|
// directly into them ourselves.
|
|
//
|
|
// Input: SrcNTE - Pointer to NTE on which packet was received.
|
|
// Packet - Packet being forwarded, used for TD.
|
|
// Data - Pointer to data buffer being forwarded.
|
|
// DataLength - Length in bytes of Data.
|
|
// BufferLength - Length in bytes available in buffer pointer to
|
|
// by Data.
|
|
// Offset - Offset into original data from which to transfer.
|
|
// LContext1, LContext2 - Context values for the link layer.
|
|
//
|
|
// Returns: NDIS_STATUS of attempt to get buffer.
|
|
//
|
|
NDIS_STATUS
|
|
GetFWBuffer(NetTableEntry * SrcNTE, PNDIS_PACKET Packet, uchar * Data,
|
|
uint DataLength, uint BufferLength, uint Offset,
|
|
NDIS_HANDLE LContext1, uint LContext2)
|
|
{
|
|
PNDIS_BUFFER FirstBuffer, CurrentBuffer;
|
|
void *DestPtr;
|
|
Interface *SrcIF;
|
|
uint FirewallMode = 0;
|
|
|
|
FirstBuffer = GetFWBufferChain(DataLength, Packet, &CurrentBuffer);
|
|
if (!FirstBuffer) {
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
uint TotalBufferSize;
|
|
PNDIS_BUFFER TempBuffer;
|
|
|
|
// Sanity check the buffer chain and packet.
|
|
TempBuffer = FirstBuffer;
|
|
TotalBufferSize = 0;
|
|
while (TempBuffer != NULL) {
|
|
TotalBufferSize += NdisBufferLength(TempBuffer);
|
|
TempBuffer = NDIS_BUFFER_LINKAGE(TempBuffer);
|
|
}
|
|
|
|
ASSERT(TotalBufferSize == DataLength);
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4127) // conditional expression is constant
|
|
NdisQueryPacket(Packet, NULL, NULL, NULL, &TotalBufferSize);
|
|
#pragma warning(pop)
|
|
|
|
ASSERT(TotalBufferSize == DataLength);
|
|
}
|
|
#endif
|
|
|
|
// First buffer points to the list of buffers we have. If we can copy the
|
|
// data here, do so, otherwise invoke the link's transfer data routine.
|
|
// if ((DataLength <= BufferLength) && (SrcNTE->nte_flags & NTE_COPY))
|
|
// change because of firewall
|
|
|
|
FirewallMode = ProcessFirewallQ();
|
|
|
|
// If DataLength is more than Lookahead size, we may need to
|
|
// call transfer data handler. If IpSec is enabled, make sure that this
|
|
// instance is not from loopback interface.
|
|
|
|
if (((DataLength <= BufferLength) && (SrcNTE->nte_flags & NTE_COPY)) ||
|
|
(FirewallMode) || (SrcNTE->nte_if->if_promiscuousmode) ||
|
|
((SrcNTE != LoopNTE) && IPSecHandlerPtr &&
|
|
RefPtrValid(&FilterRefPtr))) {
|
|
while (DataLength) {
|
|
uint CopyLength;
|
|
|
|
TcpipQueryBuffer(FirstBuffer, &DestPtr, &CopyLength, NormalPagePriority);
|
|
|
|
if (DestPtr == NULL) {
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(DestPtr, Data, CopyLength);
|
|
Data += CopyLength;
|
|
DataLength -= CopyLength;
|
|
FirstBuffer = NDIS_BUFFER_LINKAGE(FirstBuffer);
|
|
}
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
// We need to call transfer data for this.
|
|
|
|
SrcIF = SrcNTE->nte_if;
|
|
return (*(SrcIF->if_transfer)) (SrcIF->if_lcontext, LContext1, LContext2,
|
|
Offset, DataLength, Packet, &DataLength);
|
|
|
|
}
|
|
|
|
//* GetFWPacket - Get a packet for forwarding.
|
|
//
|
|
// Called when we need to get a packet to forward a datagram.
|
|
//
|
|
// Input: ReturnedPacket - Pointer to where to return a packet.
|
|
//
|
|
// Returns: Pointer to IP header buffer.
|
|
//
|
|
IPHeader *
|
|
GetFWPacket(PNDIS_PACKET *ReturnedPacket)
|
|
{
|
|
PNDIS_PACKET Packet;
|
|
|
|
Packet = FwPacketAllocate(0, 0, 0);
|
|
if (Packet) {
|
|
FWContext *FWC = (FWContext *)Packet->ProtocolReserved;
|
|
PNDIS_PACKET_EXTENSION PktExt =
|
|
NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
if (FWC->fc_pc.pc_common.pc_owner == PACKET_OWNER_IP) {
|
|
DbgPrint("Packet %x",Packet);
|
|
DbgBreakPoint();
|
|
}
|
|
FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP;
|
|
#else
|
|
ASSERT(FWC->fc_pc.pc_common.pc_owner == PACKET_OWNER_IP);
|
|
#endif
|
|
ASSERT(FWC->fc_hndisbuff);
|
|
ASSERT(FWC->fc_hbuff);
|
|
|
|
ASSERT(FWC->fc_pc.pc_pi == RtPI);
|
|
ASSERT(FWC->fc_pc.pc_context == Packet);
|
|
|
|
FWC->fc_pc.pc_common.pc_flags |= PACKET_FLAG_IPHDR;
|
|
FWC->fc_pc.pc_common.pc_IpsecCtx = NULL;
|
|
FWC->fc_pc.pc_br = NULL;
|
|
FWC->fc_pc.pc_ipsec_flags = 0;
|
|
|
|
PktExt = NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
PktExt->NdisPacketInfo[TcpIpChecksumPacketInfo] = NULL;
|
|
PktExt->NdisPacketInfo[IpSecPacketInfo] = NULL;
|
|
PktExt->NdisPacketInfo[TcpLargeSendPacketInfo] = NULL;
|
|
|
|
// Make sure that fwpackets cancel ids are initialized.
|
|
#if !MILLEN
|
|
NDIS_SET_PACKET_CANCEL_ID(Packet, NULL);
|
|
#endif
|
|
|
|
*ReturnedPacket = Packet;
|
|
|
|
return FWC->fc_hbuff;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//* IPForward / Forward a packet.
|
|
//
|
|
// The routine called when we need to forward a packet. We check if we're
|
|
// supposed to act as a gateway, and if we are and the incoming packet is a
|
|
// bcast we check and see if we're supposed to forward broadcasts. Assuming
|
|
// we're supposed to forward it, we will process any options. If we find some,
|
|
// we do some validation to make sure everything is good. After that, we look
|
|
// up the next hop. If we can't find one, we'll issue an error. Then we get
|
|
// a packet and buffers, and send it.
|
|
//
|
|
// Input: SrcNTE - NTE for net on which we received this.
|
|
// Header - Pointer to received IPheader.
|
|
// HeaderLength - Length of header.
|
|
// Data - Pointer to data to be forwarded.
|
|
// BufferLength - Length in bytes available in the buffer.
|
|
// LContext1 - lower-layer context supplied upon reception
|
|
// LContext2 - lower-layer context supplied upon reception
|
|
// DestType - Type of destination.
|
|
// MacHeadersize - Media header size
|
|
// pNdisBuffer - Pointer to NDIS_BUFFER describing the frame
|
|
// pClientCnt - Ndis return variable indicating
|
|
// if miniport buffer is pended
|
|
// LinkCtxt - contains per-link context for link-receptions
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
IPForwardPkt(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header,
|
|
uint HeaderLength, void *Data, uint BufferLength,
|
|
NDIS_HANDLE LContext1, uint LContext2, uchar DestType,
|
|
uint MacHeaderSize, PNDIS_BUFFER pNdisBuffer, uint *pClientCnt,
|
|
LinkEntry *LinkCtxt)
|
|
{
|
|
uchar *Options;
|
|
uchar OptLength;
|
|
OptIndex Index;
|
|
IPAddr DestAddr; // IP address we're routing towards.
|
|
uchar SendOnSource = DisableSendOnSource;
|
|
IPAddr NextHop; // Next hop IP address.
|
|
PNDIS_PACKET Packet;
|
|
FWContext *FWC;
|
|
IPHeader *NewHeader; // New header.
|
|
NDIS_STATUS Status;
|
|
uint DataLength;
|
|
CTELockHandle TableHandle;
|
|
uchar ErrIndex;
|
|
IPAddr OutAddr; // Address of interface we're send out on.
|
|
Interface *IF; // Interface we're sending out on.
|
|
uint MTU;
|
|
BOOLEAN HoldPkt = TRUE;
|
|
RouteCacheEntry *FwdRce;
|
|
uint FirewallMode = 0;
|
|
void *ArpCtxt = NULL;
|
|
LinkEntry *Link = NULL;
|
|
|
|
DEBUGMSG(DBG_TRACE && DBG_FWD,
|
|
(DTEXT("IPForwardPkt(%x, %x, %d, %x, %d,...)\n"),
|
|
SrcNTE, Header, HeaderLength, Data, BufferLength));
|
|
|
|
if (ForwardPackets) {
|
|
|
|
DestAddr = Header->iph_dest;
|
|
|
|
// If it's a broadcast, see if we can forward it. We won't forward it if broadcast
|
|
// forwarding is turned off, or the destination if the local (all one's) broadcast,
|
|
// or it's a multicast (Class D address). We'll pass through subnet broadcasts in
|
|
// case there's a source route. This would be odd - maybe we should disable this?
|
|
if (IS_BCAST_DEST(DestType)) {
|
|
|
|
#if IPMCAST
|
|
if (((DestType == DEST_REM_MCAST) ||
|
|
(DestType == DEST_MCAST)) &&
|
|
(g_dwMcastState == MCAST_STARTED)) {
|
|
BOOLEAN Filter;
|
|
|
|
//
|
|
// Dont forward local groups
|
|
//
|
|
|
|
if (((Header->iph_dest & 0x00FFFFFF) == 0x000000E0) ||
|
|
(Header->iph_ttl <= 1) ||
|
|
!(SrcNTE->nte_if->if_mcastflags & IPMCAST_IF_ENABLED)) {
|
|
return;
|
|
}
|
|
if (pNdisBuffer) {
|
|
Filter = IPMForwardAfterRcvPkt(SrcNTE, Header, HeaderLength,
|
|
Data, BufferLength,
|
|
LContext1, LContext2,
|
|
DestType, MacHeaderSize,
|
|
pNdisBuffer, pClientCnt,
|
|
LinkCtxt);
|
|
} else {
|
|
Filter = IPMForwardAfterRcv(SrcNTE, Header, HeaderLength,
|
|
Data, BufferLength, LContext1,
|
|
LContext2, DestType, LinkCtxt);
|
|
}
|
|
if (Filter && RefPtrValid(&FilterRefPtr)) {
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data, BufferLength);
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (!ForwardBCast) {
|
|
if (DestType > DEST_REMOTE)
|
|
IPSInfo.ipsi_inaddrerrors++;
|
|
if (RefPtrValid(&FilterRefPtr)) {
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data, BufferLength);
|
|
}
|
|
return;
|
|
}
|
|
if ((DestAddr == IP_LOCAL_BCST) ||
|
|
(DestAddr == IP_ZERO_BCST) ||
|
|
(DestType == DEST_SN_BCAST) ||
|
|
CLASSD_ADDR(DestAddr)) {
|
|
if (RefPtrValid(&FilterRefPtr)) {
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data, BufferLength);
|
|
}
|
|
return;
|
|
}
|
|
// broad cast
|
|
HoldPkt = FALSE;
|
|
} else {
|
|
|
|
FirewallMode = ProcessFirewallQ();
|
|
|
|
if ((DestType == DEST_REMOTE) && (!FirewallMode)) {
|
|
NetTableEntry* OrigNTE = SrcNTE;
|
|
SrcNTE = BestNTEForIF(Header->iph_src, SrcNTE->nte_if, FALSE);
|
|
if (SrcNTE == NULL) {
|
|
// Something bad happened.
|
|
if (RefPtrValid(&FilterRefPtr)) {
|
|
NotifyFilterOfDiscard(OrigNTE, Header, Data,
|
|
BufferLength);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// If the TTL would expire, send a message.
|
|
if (Header->iph_ttl <= 1) {
|
|
IPSInfo.ipsi_inhdrerrors++;
|
|
if (!RefPtrValid(&FilterRefPtr) ||
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data, BufferLength)) {
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_TIME_EXCEED,
|
|
TTL_IN_TRANSIT, 0, 0);
|
|
}
|
|
return;
|
|
}
|
|
DataLength = net_short(Header->iph_length) - HeaderLength;
|
|
|
|
Index.oi_srtype = NO_SR; // So we know we don't have a source route.
|
|
|
|
Index.oi_srindex = MAX_OPT_SIZE;
|
|
Index.oi_rrindex = MAX_OPT_SIZE;
|
|
Index.oi_tsindex = MAX_OPT_SIZE;
|
|
|
|
// Now check for options, and process any we find.
|
|
if (HeaderLength != sizeof(IPHeader)) {
|
|
IPOptInfo OptInfo;
|
|
|
|
RtlZeroMemory(&OptInfo, sizeof(OptInfo));
|
|
|
|
// Options and possible SR . No buffer ownership opt
|
|
HoldPkt = FALSE;
|
|
|
|
OptInfo.ioi_options = (uchar *) (Header + 1);
|
|
OptInfo.ioi_optlength = (uchar) (HeaderLength - sizeof(IPHeader));
|
|
// Validate options, and set up indices.
|
|
if ((ErrIndex = ParseRcvdOptions(&OptInfo, &Index)) < MAX_OPT_SIZE) {
|
|
IPSInfo.ipsi_inhdrerrors++;
|
|
if (!RefPtrValid(&FilterRefPtr) ||
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data, BufferLength)) {
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM,
|
|
PTR_VALID, ((uint)ErrIndex + sizeof(IPHeader)), 0);
|
|
}
|
|
return;
|
|
}
|
|
// If source routing option was set, and source routing is disabled,
|
|
// then drop the packet.
|
|
if ((OptInfo.ioi_flags & IP_FLAG_SSRR) && DisableIPSourceRouting) {
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Pkt dropped - Source routing disabled\n"));
|
|
if (RefPtrValid(&FilterRefPtr)) {
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data, BufferLength);
|
|
}
|
|
return;
|
|
}
|
|
Options = CTEAllocMemN(OptInfo.ioi_optlength, 'IiCT');
|
|
if (!Options) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return; // Couldn't get an
|
|
|
|
} // option buffer, return;
|
|
|
|
// Now copy into our buffer.
|
|
RtlCopyMemory(Options, OptInfo.ioi_options, OptLength = OptInfo.ioi_optlength);
|
|
|
|
// See if we have a source routing option, and if so we may need to process it. If
|
|
// we have one, and the destination in the header is us, we need to update the
|
|
// route and the header.
|
|
if (Index.oi_srindex != MAX_OPT_SIZE) {
|
|
if (DestType >= DEST_REMOTE) { // Not for us.
|
|
|
|
if (Index.oi_srtype == IP_OPT_SSRR) {
|
|
// This packet is strict source routed, but we're not
|
|
// the destination! We can't continue from here -
|
|
// perhaps we should send an ICMP, but I'm not sure
|
|
// which one it would be.
|
|
CTEFreeMem(Options);
|
|
IPSInfo.ipsi_inaddrerrors++;
|
|
if (RefPtrValid(&FilterRefPtr)) {
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data,
|
|
BufferLength);
|
|
}
|
|
return;
|
|
}
|
|
Index.oi_srindex = MAX_OPT_SIZE; // Don't need to update this.
|
|
|
|
} else { // This came here, we need to update the destination address.
|
|
|
|
uchar *SROpt = Options + Index.oi_srindex;
|
|
uchar Pointer;
|
|
|
|
Pointer = SROpt[IP_OPT_PTR] - 1; // Index starts from one.
|
|
|
|
// Get the next hop address, and see if it's a broadcast.
|
|
DestAddr = *(IPAddr UNALIGNED *) & SROpt[Pointer];
|
|
DestType = GetAddrType(DestAddr); // Find address type.
|
|
|
|
if (IS_BCAST_DEST(DestType)) {
|
|
|
|
if (!RefPtrValid(&FilterRefPtr) ||
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data,
|
|
BufferLength)) {
|
|
SendICMPErr(SrcNTE->nte_addr, Header,
|
|
ICMP_DEST_UNREACH, SR_FAILED, 0, 0);
|
|
}
|
|
IPSInfo.ipsi_inhdrerrors++;
|
|
CTEFreeMem(Options);
|
|
return;
|
|
}
|
|
// If we came through here, any sort of broadcast needs
|
|
// to be sent out the way it came, so update that flag.
|
|
SendOnSource = EnableSendOnSource;
|
|
}
|
|
}
|
|
} else { // No options.
|
|
|
|
Options = (uchar *) NULL;
|
|
OptLength = 0;
|
|
}
|
|
|
|
IPSInfo.ipsi_forwdatagrams++;
|
|
|
|
// We've processed the options. Now look up the next hop. If we can't
|
|
// find one, send back an error.
|
|
IF = LookupForwardingNextHop(DestAddr, Header->iph_src, &NextHop, &MTU,
|
|
Header->iph_protocol, (uchar *) Data,
|
|
BufferLength, &FwdRce, &Link,
|
|
Header->iph_src);
|
|
|
|
if (IF == NULL) {
|
|
// Couldn't find an outgoing route.
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
if (!RefPtrValid(&FilterRefPtr) ||
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data, BufferLength)) {
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
|
|
HOST_UNREACH, 0, 0);
|
|
}
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
return;
|
|
} else {
|
|
if (IF->if_flags & IF_FLAGS_P2MP) {
|
|
ASSERT(Link);
|
|
if (Link) {
|
|
ArpCtxt = Link->link_arpctxt;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the DF flag is set, make sure the packet doesn't need
|
|
// fragmentation. If this is the case, send an ICMP error
|
|
// now while we still have the original IP header. The ICMP
|
|
// message includes the MTU so the source host can perform
|
|
// Path MTU discovery.
|
|
//
|
|
if ((Header->iph_offset & IP_DF_FLAG) &&
|
|
((DataLength + (uint) OptLength) > MTU)) {
|
|
ASSERT((MTU + sizeof(IPHeader)) >= 68);
|
|
ASSERT((MTU + sizeof(IPHeader)) <= 0xFFFF);
|
|
|
|
IPSInfo.ipsi_fragfails++;
|
|
if (!RefPtrValid(&FilterRefPtr) ||
|
|
NotifyFilterOfDiscard(SrcNTE, Header, Data, BufferLength)) {
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
|
|
FRAG_NEEDED,
|
|
net_long((ulong)(MTU + sizeof(IPHeader))), 0);
|
|
}
|
|
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
DerefIF(IF);
|
|
return;
|
|
}
|
|
if (DataLength > MTU) {
|
|
|
|
HoldPkt = FALSE;
|
|
}
|
|
|
|
// If there is no ipsec policy, it is safe to
|
|
// reuse the indicated mdl chain.
|
|
|
|
if (IPSecStatus) {
|
|
HoldPkt = FALSE;
|
|
}
|
|
|
|
// See if we need to filter this packet. If we do, call the filter routine
|
|
// to see if it's OK to forward it.
|
|
if (RefPtrValid(&FilterRefPtr)) {
|
|
Interface *InIF = SrcNTE->nte_if;
|
|
uint InIFIndex;
|
|
IPAddr InLinkNextHop;
|
|
IPAddr OutLinkNextHop;
|
|
FORWARD_ACTION Action;
|
|
IPPacketFilterPtr FilterPtr;
|
|
uint FirewallMode = 0;
|
|
|
|
FirewallMode = ProcessFirewallQ();
|
|
|
|
if (FirewallMode) {
|
|
InIFIndex = INVALID_IF_INDEX;
|
|
InLinkNextHop = NULL_IP_ADDR;
|
|
} else {
|
|
InIFIndex = InIF->if_index;
|
|
if ((InIF->if_flags & IF_FLAGS_P2MP) && LinkCtxt) {
|
|
InLinkNextHop = LinkCtxt->link_NextHop;
|
|
} else {
|
|
InLinkNextHop = NULL_IP_ADDR;
|
|
}
|
|
}
|
|
|
|
if ((IF->if_flags & IF_FLAGS_P2MP) && Link) {
|
|
OutLinkNextHop = Link->link_NextHop;
|
|
} else {
|
|
OutLinkNextHop = NULL_IP_ADDR;
|
|
}
|
|
|
|
FilterPtr = AcquireRefPtr(&FilterRefPtr);
|
|
Action = (*FilterPtr) (Header, Data, BufferLength,
|
|
InIFIndex, IF->if_index,
|
|
InLinkNextHop, OutLinkNextHop);
|
|
ReleaseRefPtr(&FilterRefPtr);
|
|
|
|
if (Action != FORWARD) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
DerefIF(IF);
|
|
|
|
#if FFP_SUPPORT
|
|
// Seed a -ve FFP entry; Packet henceforth dropped in NIC Driver
|
|
TCPTRACE(("Filter dropped a packet, Seeding -ve cache entry\n"));
|
|
IPSetInFFPCaches(Header, Data, BufferLength, (ULONG) FFP_DISCARD_PACKET);
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
// If we have a strict source route and the next hop is not the one
|
|
// specified, send back an error.
|
|
if (Index.oi_srtype == IP_OPT_SSRR) {
|
|
if (DestAddr != NextHop) {
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
|
|
SR_FAILED, 0, 0);
|
|
CTEFreeMem(Options);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
DerefIF(IF);
|
|
return;
|
|
}
|
|
}
|
|
// Update the options, if we can and we need to.
|
|
if ((DestType != DEST_BCAST) && Options != NULL) {
|
|
NetTableEntry *OutNTE;
|
|
|
|
// Need to find a valid source address for the outgoing interface.
|
|
CTEGetLock(&RouteTableLock.Lock, &TableHandle);
|
|
OutNTE = BestNTEForIF(DestAddr, IF, FALSE);
|
|
if (OutNTE == NULL) {
|
|
// No NTE for this IF. Something's wrong, just bail out.
|
|
CTEFreeLock(&RouteTableLock.Lock, TableHandle);
|
|
CTEFreeMem(Options);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
DerefIF(IF);
|
|
return;
|
|
} else {
|
|
OutAddr = OutNTE->nte_addr;
|
|
CTEFreeLock(&RouteTableLock.Lock, TableHandle);
|
|
}
|
|
|
|
ErrIndex = UpdateOptions(Options, &Index,
|
|
(IP_LOOPBACK(OutAddr) ? DestAddr : OutAddr));
|
|
|
|
if (ErrIndex != MAX_OPT_SIZE) {
|
|
IPSInfo.ipsi_inhdrerrors++;
|
|
SendICMPErr(OutAddr, Header, ICMP_PARAM_PROBLEM, PTR_VALID,
|
|
((ulong) ErrIndex + sizeof(IPHeader)), 0);
|
|
CTEFreeMem(Options);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
DerefIF(IF);
|
|
return;
|
|
}
|
|
}
|
|
// Send a redirect, if we need to. We'll send a redirect if the packet
|
|
// is going out on the interface it came in on and the next hop address
|
|
// is on the same subnet as the NTE we received it on, and if there
|
|
// are no source route options. We also need to make sure that the
|
|
// source of the datagram is on the I/F we received it on, so we don't
|
|
// send a redirect to another gateway.
|
|
// SendICMPErr will check and not send a redirect if this is a broadcast.
|
|
if ((SrcNTE->nte_if == IF) &&
|
|
IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask,
|
|
NextHop & SrcNTE->nte_mask) &&
|
|
IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask,
|
|
Header->iph_src & SrcNTE->nte_mask)) {
|
|
if (Index.oi_srindex == MAX_OPT_SIZE) {
|
|
|
|
#ifdef REDIRECT_DEBUG
|
|
|
|
#define PR_IP_ADDR(x) \
|
|
((x)&0x000000ff),(((x)&0x0000ff00)>>8),(((x)&0x00ff0000)>>16),(((x)&0xff000000)>>24)
|
|
|
|
DbgPrint("IP: Sending Redirect. IF = %x SRC_NTE = %x SrcNteIF = %x\n",
|
|
IF, SrcNTE, SrcNTE->nte_if);
|
|
|
|
DbgPrint("IP: SrcNteAddr = %d.%d.%d.%d Mask = %d.%d.%d.%d\n",
|
|
PR_IP_ADDR(SrcNTE->nte_addr), PR_IP_ADDR(SrcNTE->nte_mask));
|
|
|
|
DbgPrint("IP: NextHop = %d.%d.%d.%d Header Src = %d.%d.%d.%d, Dst = %d.%d.%d.%d\n",
|
|
PR_IP_ADDR(NextHop),
|
|
PR_IP_ADDR(Header->iph_src),
|
|
PR_IP_ADDR(Header->iph_dest));
|
|
|
|
#endif
|
|
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_REDIRECT,
|
|
REDIRECT_HOST, NextHop, 0);
|
|
}
|
|
}
|
|
// We have the next hop. Now get a forwarding packet.
|
|
if ((NewHeader = GetFWPacket(&Packet)) != NULL) {
|
|
|
|
Packet->Private.Flags |= NDIS_PROTOCOL_ID_TCP_IP;
|
|
// Save the packet forwarding context info.
|
|
FWC = (FWContext *) Packet->ProtocolReserved;
|
|
FWC->fc_options = Options;
|
|
FWC->fc_optlength = OptLength;
|
|
FWC->fc_if = IF;
|
|
FWC->fc_mtu = MTU;
|
|
FWC->fc_srcnte = SrcNTE;
|
|
FWC->fc_nexthop = NextHop;
|
|
FWC->fc_sos = SendOnSource;
|
|
FWC->fc_dtype = DestType;
|
|
FWC->fc_index = Index;
|
|
FWC->fc_iflink = Link;
|
|
|
|
if (pNdisBuffer && HoldPkt &&
|
|
(NDIS_GET_PACKET_STATUS((PNDIS_PACKET) LContext1) != NDIS_STATUS_RESOURCES)) {
|
|
uint xsum;
|
|
|
|
DEBUGMSG(DBG_INFO && DBG_FWD,
|
|
(DTEXT("IPForwardPkt: bufown %x\n"), pNdisBuffer));
|
|
|
|
// Buffer transfer possible!
|
|
|
|
//ASSERT(LContext2 <= 8);
|
|
|
|
MacHeaderSize += LContext2;
|
|
|
|
// remember the original Packet and mac hdr size
|
|
|
|
FWC->fc_bufown = LContext1;
|
|
FWC->fc_MacHdrSize = MacHeaderSize;
|
|
|
|
//Munge ttl and xsum fields
|
|
|
|
Header->iph_ttl = Header->iph_ttl - 1;
|
|
|
|
xsum = Header->iph_xsum + 1;
|
|
|
|
//add carry
|
|
Header->iph_xsum = (ushort)(xsum + (xsum >> 16));
|
|
|
|
|
|
// Adjust incoming mdl pointer and counts
|
|
|
|
NdisAdjustBuffer(
|
|
pNdisBuffer,
|
|
(PCHAR) NdisBufferVirtualAddress(pNdisBuffer) + MacHeaderSize,
|
|
NdisBufferLength(pNdisBuffer) - MacHeaderSize);
|
|
|
|
//Now link this mdl to the packet
|
|
|
|
Packet->Private.Head = pNdisBuffer;
|
|
Packet->Private.Tail = pNdisBuffer;
|
|
|
|
Packet->Private.TotalLength = DataLength + HeaderLength;
|
|
Packet->Private.Count = 1;
|
|
|
|
// We never loopback the packet
|
|
// except if we are in promiscuous mode
|
|
if (!IF->if_promiscuousmode) {
|
|
NdisSetPacketFlags(Packet, NDIS_FLAGS_DONT_LOOPBACK);
|
|
}
|
|
|
|
Status = (*(IF->if_xmit)) (IF->if_lcontext, &Packet, 1,
|
|
NextHop, FwdRce, ArpCtxt);
|
|
|
|
DbgNumPktFwd++;
|
|
|
|
if (Status != NDIS_STATUS_PENDING) {
|
|
NdisAdjustBuffer(
|
|
pNdisBuffer,
|
|
(PCHAR) NdisBufferVirtualAddress(pNdisBuffer) - MacHeaderSize,
|
|
NdisBufferLength(pNdisBuffer) + MacHeaderSize);
|
|
|
|
Packet->Private.Head = NULL;
|
|
Packet->Private.Tail = NULL;
|
|
|
|
FWC->fc_bufown = NULL;
|
|
#if MCAST_BUG_TRACKING
|
|
FWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(Packet);
|
|
*pClientCnt = 0;
|
|
} else {
|
|
//Okay, the xmit is pending indicate this to ndis.
|
|
*pClientCnt = 1;
|
|
}
|
|
|
|
return;
|
|
|
|
} else {
|
|
FWC->fc_bufown = NULL;
|
|
}
|
|
|
|
// Fill in the header in the forwarding context
|
|
|
|
NewHeader->iph_verlen = Header->iph_verlen;
|
|
NewHeader->iph_tos = Header->iph_tos;
|
|
NewHeader->iph_length = Header->iph_length;
|
|
NewHeader->iph_id = Header->iph_id;
|
|
NewHeader->iph_offset = Header->iph_offset;
|
|
NewHeader->iph_protocol = Header->iph_protocol;
|
|
NewHeader->iph_src = Header->iph_src;
|
|
|
|
NewHeader->iph_dest = DestAddr;
|
|
NewHeader->iph_ttl = Header->iph_ttl - 1;
|
|
NewHeader->iph_xsum = 0;
|
|
|
|
// Now that we have a packet, go ahead and transfer data the
|
|
// data in if we need to.
|
|
if (DataLength == 0) {
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
} else {
|
|
Status = GetFWBuffer(SrcNTE, Packet, Data, DataLength,
|
|
BufferLength, HeaderLength, LContext1,
|
|
LContext2);
|
|
}
|
|
|
|
// If the status is pending, don't do anything now. Otherwise,
|
|
// if the status is success send the packet.
|
|
if (Status != NDIS_STATUS_PENDING)
|
|
if (Status == NDIS_STATUS_SUCCESS) {
|
|
|
|
if (!IF->if_promiscuousmode) {
|
|
NdisSetPacketFlags(Packet, NDIS_FLAGS_DONT_LOOPBACK);
|
|
}
|
|
SendFWPacket(Packet, Status, DataLength);
|
|
} else {
|
|
// Some sort of failure. Free the packet.
|
|
IPSInfo.ipsi_outdiscards++;
|
|
#if MCAST_BUG_TRACKING
|
|
FWC->fc_mtu = __LINE__;
|
|
#endif
|
|
FreeFWPacket(Packet);
|
|
}
|
|
} else { // Couldn't get a packet, so drop this.
|
|
|
|
DEBUGMSG(DBG_ERROR && DBG_FWD,
|
|
(DTEXT("IPForwardPkt: failed to get a forwarding packet!\n")));
|
|
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
if (Link) {
|
|
DerefLink(Link);
|
|
}
|
|
DerefIF(IF);
|
|
}
|
|
} else { // Forward called, but forwarding turned off.
|
|
|
|
DEBUGMSG(DBG_WARN && DBG_FWD,
|
|
(DTEXT("IPForwardPkt: Forwarding called but is actually OFF.\n")));
|
|
|
|
if (DestType != DEST_BCAST && DestType != DEST_SN_BCAST) {
|
|
// No need to go through here for strictly broadcast packets,
|
|
// although we want to bump the counters for remote bcast stuff.
|
|
IPSInfo.ipsi_inaddrerrors++;
|
|
|
|
if (!IS_BCAST_DEST(DestType)) {
|
|
if (DestType == DEST_LOCAL) // Called when local, must be SR.
|
|
|
|
SendICMPErr(SrcNTE->nte_addr, Header,
|
|
ICMP_DEST_UNREACH, SR_FAILED, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//* AddNTERoutes - Add the routes for an NTE.
|
|
//
|
|
// Called during initalization or during DHCP address assignment to add
|
|
// routes. We add routes for the address of the NTE, including routes
|
|
// to the subnet and the address itself.
|
|
//
|
|
// Input: NTE - NTE for which to add routes.
|
|
//
|
|
// Returns: TRUE if they were all added, FALSE if not.
|
|
//
|
|
uint
|
|
AddNTERoutes(NetTableEntry * NTE)
|
|
{
|
|
IPMask Mask, SNMask;
|
|
Interface *IF;
|
|
CTELockHandle Handle;
|
|
IPAddr AllSNBCast;
|
|
IP_STATUS Status;
|
|
IPRouteNotifyOutput RNO = {0};
|
|
|
|
// First, add the route to the address itself. This is a route through
|
|
// the loopback interface.
|
|
|
|
#if DBG
|
|
IF_IPDBG(IP_DEBUG_ADDRESS)
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
" AddNTE: Adding host route for %x\n", NTE->nte_addr));
|
|
#endif
|
|
|
|
IF = NTE->nte_if;
|
|
|
|
if (AddRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL, LoopNTE->nte_if,
|
|
LOOPBACK_MSS, IF->if_metric, IRE_PROTO_LOCAL, ATYPE_OVERRIDE,
|
|
0, 0) != IP_SUCCESS)
|
|
return FALSE;
|
|
|
|
Mask = IPNetMask(NTE->nte_addr);
|
|
|
|
// Now add the route for the all-subnet's broadcast, if one doesn't already
|
|
// exist. There is special case code to handle this in SendIPBCast, so the
|
|
// exact interface we add this on doesn't really matter.
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &Handle);
|
|
AllSNBCast = (NTE->nte_addr & Mask) | (IF->if_bcast & ~Mask);
|
|
|
|
#if DBG
|
|
IF_IPDBG(IP_DEBUG_ADDRESS)
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
" AddNTE: SNBCast address %x\n", AllSNBCast));
|
|
#endif
|
|
|
|
Status = LockedAddRoute(AllSNBCast, HOST_MASK, IPADDR_LOCAL, IF,
|
|
NTE->nte_mss, IF->if_metric, IRE_PROTO_LOCAL,
|
|
ATYPE_PERM, 0, FALSE, &RNO);
|
|
CTEFreeLock(&RouteTableLock.Lock, Handle);
|
|
|
|
if (Status != IP_SUCCESS) {
|
|
return FALSE;
|
|
} else if (RNO.irno_ifindex) {
|
|
RtChangeNotifyEx(&RNO);
|
|
RtChangeNotify(&RNO);
|
|
}
|
|
|
|
// If we're doing IGMP, add the route to the multicast address.
|
|
if (IGMPLevel != 0) {
|
|
|
|
#if DBG
|
|
IF_IPDBG(IP_DEBUG_ADDRESS)
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
" AddNTE: Adding classD address\n"));
|
|
#endif
|
|
|
|
if (AddRoute(MCAST_DEST, CLASSD_MASK, IPADDR_LOCAL, NTE->nte_if,
|
|
NTE->nte_mss, IF->if_metric, IRE_PROTO_LOCAL, ATYPE_PERM,
|
|
0, 0) != IP_SUCCESS)
|
|
return FALSE;
|
|
}
|
|
if (NTE->nte_mask != HOST_MASK) {
|
|
// And finally the route to the subnet.
|
|
SNMask = NTE->nte_mask;
|
|
|
|
#if DBG
|
|
IF_IPDBG(IP_DEBUG_ADDRESS)
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
|
|
" AddNTE: Adding subnet route %x\n",
|
|
NTE->nte_addr & SNMask));
|
|
#endif
|
|
|
|
if (AddRoute(NTE->nte_addr & SNMask, SNMask, IPADDR_LOCAL, NTE->nte_if,
|
|
NTE->nte_mss, IF->if_metric, IRE_PROTO_LOCAL, ATYPE_PERM,
|
|
0, 0) != IP_SUCCESS)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* DelNTERoutes - Add the routes for an NTE.
|
|
//
|
|
// Called when we receive media disconnect indication.
|
|
// routes.
|
|
//
|
|
// Input: NTE - NTE for which to delete routes.
|
|
//
|
|
// Returns: TRUE if they were all deleted, FALSE if not.
|
|
//
|
|
uint
|
|
DelNTERoutes(NetTableEntry * NTE)
|
|
{
|
|
IPMask SNMask;
|
|
uint retVal;
|
|
|
|
retVal = TRUE;
|
|
|
|
// First, delete the route to the address itself. This is a route through
|
|
// the loopback interface.
|
|
if (DeleteRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL, LoopNTE->nte_if, 0) != IP_SUCCESS)
|
|
retVal = FALSE;
|
|
|
|
// If we're doing IGMP, add the route to the multicast address.
|
|
if (IGMPLevel != 0) {
|
|
if (!(NTE->nte_flags & NTE_IF_DELETING) &&
|
|
(NTE->nte_if->if_ntecount == 0)) { // this is the last NTE on this if
|
|
|
|
if (DeleteRoute(MCAST_DEST, CLASSD_MASK, IPADDR_LOCAL, NTE->nte_if, 0) != IP_SUCCESS)
|
|
retVal = FALSE;
|
|
}
|
|
}
|
|
if (NTE->nte_mask != HOST_MASK) {
|
|
// And finally the route to the subnet.
|
|
// if there are no other NTEs on IF for the same subnet route
|
|
|
|
NetTableEntry *tmpNTE = NTE->nte_if->if_nte;
|
|
|
|
while (tmpNTE) {
|
|
|
|
if ((tmpNTE != NTE) && (tmpNTE->nte_flags & NTE_VALID) && ((tmpNTE->nte_addr & tmpNTE->nte_mask) == (NTE->nte_addr & NTE->nte_mask))) {
|
|
break;
|
|
}
|
|
tmpNTE = tmpNTE->nte_ifnext;
|
|
|
|
}
|
|
|
|
if (!tmpNTE) {
|
|
|
|
SNMask = NTE->nte_mask;
|
|
|
|
if (DeleteRoute(NTE->nte_addr & SNMask, SNMask, IPADDR_LOCAL, NTE->nte_if, 0) != IP_SUCCESS)
|
|
retVal = FALSE;
|
|
|
|
}
|
|
}
|
|
if (!(NTE->nte_flags & NTE_IF_DELETING)) {
|
|
Interface *IF = NTE->nte_if;
|
|
NetTableEntry *tmpNTE = IF->if_nte;
|
|
IPMask Mask;
|
|
IPAddr AllSNBCast;
|
|
|
|
Mask = IPNetMask(NTE->nte_addr);
|
|
|
|
AllSNBCast = (NTE->nte_addr & Mask) | (IF->if_bcast & ~Mask);
|
|
|
|
while (tmpNTE) {
|
|
IPMask tmpMask;
|
|
IPAddr tmpAllSNBCast;
|
|
|
|
tmpMask = IPNetMask(tmpNTE->nte_addr);
|
|
|
|
tmpAllSNBCast = (tmpNTE->nte_addr & tmpMask) | (IF->if_bcast & ~tmpMask);
|
|
|
|
if ((tmpNTE != NTE) && (tmpNTE->nte_flags & NTE_VALID) && IP_ADDR_EQUAL(AllSNBCast, tmpAllSNBCast)) {
|
|
break;
|
|
}
|
|
tmpNTE = tmpNTE->nte_ifnext;
|
|
}
|
|
|
|
if (!tmpNTE) {
|
|
// Delete the route for the all-subnet's broadcast.
|
|
if (DeleteRoute(AllSNBCast, HOST_MASK, IPADDR_LOCAL, IF, 0) != IP_SUCCESS)
|
|
retVal = FALSE;
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
//* DelIFRoutes - Delete the routes for an interface.
|
|
//
|
|
// Called when we receive media disconnect indication.
|
|
// routes.
|
|
//
|
|
// Input: IF - IF for which to delete routes.
|
|
//
|
|
// Returns: TRUE if they were all deleted, FALSE if not.
|
|
//
|
|
uint
|
|
DelIFRoutes(Interface * IF)
|
|
{
|
|
NetTableEntry *NTE;
|
|
uint i;
|
|
|
|
for (i = 0; i < NET_TABLE_SIZE; i++) {
|
|
NetTableEntry *NetTableList = NewNetTableList[i];
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if ((NTE->nte_flags & NTE_VALID) && NTE->nte_if == IF) {
|
|
|
|
// This guy is on the interface, and needs to be deleted.
|
|
if (!DelNTERoutes(NTE)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//* AddIFRoutes - Add the routes for an interface.
|
|
//
|
|
// Called when we receive media disconnect indication.
|
|
// routes.
|
|
//
|
|
// Input: IF - IF for which to Add routes.
|
|
//
|
|
// Returns: TRUE if they were all Added, FALSE if not.
|
|
//
|
|
uint
|
|
AddIFRoutes(Interface * IF)
|
|
{
|
|
NetTableEntry *NTE;
|
|
uint i;
|
|
|
|
for (i = 0; i < NET_TABLE_SIZE; i++) {
|
|
NetTableEntry *NetTableList = NewNetTableList[i];
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if ((NTE->nte_flags & NTE_VALID) && NTE->nte_if == IF) {
|
|
|
|
// This guy is on the interface, and needs to be added.
|
|
if (!AddNTERoutes(NTE)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#pragma BEGIN_INIT
|
|
|
|
uint BCastMinMTU = 0xffff;
|
|
|
|
//* InitNTERouting - do per NTE route initialization.
|
|
//
|
|
// Called when we need to initialize per-NTE routing. For the specified NTE,
|
|
// call AddNTERoutes to add a route for a net bcast, subnet bcast, and local
|
|
// attached subnet. The net bcast entry is sort of a filler - net and
|
|
// global bcasts are always handled specially. For this reason we specify
|
|
// the FirstInterface when adding the route. Subnet bcasts are assumed to
|
|
// only go out on one interface, so the actual interface to be used is
|
|
// specifed. If two interfaces are on the same subnet the last interface is
|
|
// the one that will be used.
|
|
//
|
|
// Input: NTE - NTE for which routing is to be initialized.
|
|
// NumGWs - Number of default gateways to add.
|
|
// GWList - List of default gateways.
|
|
// GWMetricList - the metric for each gateway.
|
|
//
|
|
// Returns: TRUE if we succeed, FALSE if we don't.
|
|
//
|
|
uint
|
|
InitNTERouting(NetTableEntry * NTE, uint NumGWs, IPAddr * GWList,
|
|
uint * GWMetricList)
|
|
{
|
|
uint i;
|
|
Interface *IF;
|
|
|
|
if (NTE != LoopNTE) {
|
|
BCastMinMTU = MIN(BCastMinMTU, NTE->nte_mss);
|
|
|
|
IF = NTE->nte_if;
|
|
AddRoute(IF->if_bcast, HOST_MASK, IPADDR_LOCAL, IF,
|
|
BCastMinMTU, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE,
|
|
0, 0); // Route for local
|
|
// bcast.
|
|
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
if (!AddNTERoutes(NTE))
|
|
return FALSE;
|
|
|
|
// Now add the default routes that are present on this net. We
|
|
// don't check for errors here, but we should probably
|
|
// log an error.
|
|
for (i = 0; i < NumGWs; i++) {
|
|
IPAddr GWAddr;
|
|
|
|
GWAddr = net_long(GWList[i]);
|
|
|
|
if (IP_ADDR_EQUAL(GWAddr, NTE->nte_addr)) {
|
|
GWAddr = IPADDR_LOCAL;
|
|
}
|
|
|
|
AddRoute(NULL_IP_ADDR, DEFAULT_MASK,
|
|
GWAddr, NTE->nte_if, NTE->nte_mss,
|
|
GWMetricList[i] ? GWMetricList[i] : IF->if_metric,
|
|
IRE_PROTO_NETMGMT, ATYPE_OVERRIDE, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//* EnableRouter - enables forwarding.
|
|
//
|
|
// This routine configures this node to enable packet-forwarding.
|
|
// It must be called with the route table lock held.
|
|
//
|
|
// Entry:
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
EnableRouter()
|
|
{
|
|
RouterConfigured = TRUE;
|
|
ForwardBCast = FALSE;
|
|
ForwardPackets = TRUE;
|
|
}
|
|
|
|
//* DisableRouter - disables forwarding.
|
|
//
|
|
// This routine configures this node to disable packet-forwarding.
|
|
// It must be called with the route table lock held.
|
|
//
|
|
// Entry:
|
|
//
|
|
// Returns: nothing.
|
|
//
|
|
void
|
|
DisableRouter()
|
|
{
|
|
RouterConfigured = FALSE;
|
|
ForwardBCast = FALSE;
|
|
ForwardPackets = FALSE;
|
|
}
|
|
|
|
//* IPEnableRouterWithRefCount - acquires or releases a reference to forwarding
|
|
//
|
|
// This routine increments or decrements the reference-count on forwarding
|
|
// functionality. When the first reference is acquired, forwarding is enabled.
|
|
// When the last reference is released, forwarding is disabled.
|
|
// It must be called with the route table lock held.
|
|
//
|
|
// Entry: Enable - indicates whether to acquire or release a reference
|
|
//
|
|
// Return: the number of remaining references.
|
|
//
|
|
int
|
|
IPEnableRouterWithRefCount(LOGICAL Enable)
|
|
{
|
|
if (Enable) {
|
|
if (++IPEnableRouterRefCount == 1 && !RouterConfigured) {
|
|
EnableRouter();
|
|
}
|
|
} else {
|
|
if (--IPEnableRouterRefCount == 0 && RouterConfigured) {
|
|
DisableRouter();
|
|
}
|
|
}
|
|
return IPEnableRouterRefCount;
|
|
}
|
|
|
|
//* InitRouting - Initialize our routing table.
|
|
//
|
|
// Called during initialization to initialize the routing table.
|
|
//
|
|
// Entry: Nothing.
|
|
//
|
|
// Returns: True if we succeeded, False if we didn't.
|
|
//
|
|
int
|
|
InitRouting(IPConfigInfo * ci)
|
|
{
|
|
UINT initStatus;
|
|
ULONG initFlags;
|
|
|
|
CTEInitLock(&RouteTableLock.Lock);
|
|
InitRefPtr(&FilterRefPtr, &RouteTableLock.Lock, DummyFilterPtr);
|
|
InitRefPtr(&DODRefPtr, &RouteTableLock.Lock, DummyDODCallout);
|
|
|
|
DefGWConfigured = 0;
|
|
DefGWActive = 0;
|
|
|
|
RtlZeroMemory(&DummyInterface, sizeof(DummyInterface));
|
|
DummyInterface.ri_if.if_xmit = DummyXmit;
|
|
DummyInterface.ri_if.if_transfer = DummyXfer;
|
|
DummyInterface.ri_if.if_close = DummyClose;
|
|
DummyInterface.ri_if.if_invalidate = DummyInvalidate;
|
|
DummyInterface.ri_if.if_qinfo = DummyQInfo;
|
|
DummyInterface.ri_if.if_setinfo = DummySetInfo;
|
|
DummyInterface.ri_if.if_getelist = DummyGetEList;
|
|
DummyInterface.ri_if.if_addaddr = DummyAddAddr;
|
|
DummyInterface.ri_if.if_deladdr = DummyDelAddr;
|
|
DummyInterface.ri_if.if_dondisreq = DummyDoNdisReq;
|
|
DummyInterface.ri_if.if_bcast = IP_LOCAL_BCST;
|
|
DummyInterface.ri_if.if_speed = 10000000;
|
|
DummyInterface.ri_if.if_mtu = 1500;
|
|
DummyInterface.ri_if.if_index = INVALID_IF_INDEX;
|
|
LOCKED_REFERENCE_IF(&DummyInterface.ri_if);
|
|
DummyInterface.ri_if.if_pnpcontext = 0;
|
|
|
|
initFlags = ci->ici_fastroutelookup ? TFLAG_FAST_TRIE_ENABLED : 0;
|
|
if ((initStatus = InitRouteTable(initFlags,
|
|
ci->ici_fastlookuplevels,
|
|
ci->ici_maxfastlookupmemory,
|
|
ci->ici_maxnormlookupmemory))
|
|
!= STATUS_SUCCESS) {
|
|
TCPTRACE(("Init Route Table Failed: %08x\n", initStatus));
|
|
return FALSE;
|
|
}
|
|
|
|
// We've created at least one net. We need to add routing table entries for
|
|
// the global broadcast address, as well as for subnet and net broadcasts,
|
|
// and routing entries for the local subnet. We alse need to add a loopback
|
|
// route for the loopback net. Below, we'll add a host route for ourselves
|
|
// through the loopback net.
|
|
AddRoute(LOOPBACK_ADDR & CLASSA_MASK, CLASSA_MASK, IPADDR_LOCAL,
|
|
LoopNTE->nte_if, LOOPBACK_MSS, 1, IRE_PROTO_LOCAL, ATYPE_PERM,
|
|
0, 0);
|
|
|
|
// Route for loopback.
|
|
if ((uchar) ci->ici_gateway) {
|
|
EnableRouter();
|
|
}
|
|
CTEInitTimer(&IPRouteTimer);
|
|
RouteTimerTicks = 0;
|
|
#if FFP_SUPPORT
|
|
FFPFlushRequired = FALSE;
|
|
#endif
|
|
FlushIFTimerTicks = 0;
|
|
|
|
CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL);
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
PVOID
|
|
NTAPI
|
|
FwPacketAllocate (
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
NDIS_STATUS Status;
|
|
PNDIS_PACKET Packet;
|
|
|
|
UNREFERENCED_PARAMETER(PoolType);
|
|
UNREFERENCED_PARAMETER(NumberOfBytes);
|
|
UNREFERENCED_PARAMETER(Tag);
|
|
|
|
// Get a packet from our forwarding packet pool.
|
|
//
|
|
NdisAllocatePacket(&Status, &Packet, IpForwardPacketPool);
|
|
if (Status == NDIS_STATUS_SUCCESS) {
|
|
PNDIS_BUFFER Buffer;
|
|
IPHeader *Header;
|
|
|
|
// Get an IP header buffer from our IP header pool.
|
|
//
|
|
Buffer = MdpAllocate(IpHeaderPool, &Header);
|
|
if (Buffer) {
|
|
FWContext *FWC = (FWContext *)Packet->ProtocolReserved;
|
|
|
|
// Intialize the fowarding context area of the packet.
|
|
//
|
|
RtlZeroMemory(FWC, sizeof(FWContext));
|
|
FWC->fc_hndisbuff = Buffer;
|
|
FWC->fc_hbuff = Header;
|
|
FWC->fc_pc.pc_common.pc_flags = PACKET_FLAG_FW | PACKET_FLAG_IPHDR;
|
|
|
|
#if MCAST_BUG_TRACKING
|
|
FWC->fc_pc.pc_common.pc_owner = 0;
|
|
#else
|
|
FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP;
|
|
#endif
|
|
FWC->fc_pc.pc_pi = RtPI;
|
|
FWC->fc_pc.pc_context = Packet;
|
|
|
|
return Packet;
|
|
}
|
|
|
|
NdisFreePacket(Packet);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
NTAPI
|
|
FwPacketFree (
|
|
IN PVOID Buffer
|
|
)
|
|
{
|
|
PNDIS_PACKET Packet = (PNDIS_PACKET)Buffer;
|
|
FWContext *FWC = (FWContext *)Packet->ProtocolReserved;
|
|
|
|
// Return any IP header to its pool.
|
|
//
|
|
if (FWC->fc_hndisbuff) {
|
|
MdpFree(FWC->fc_hndisbuff);
|
|
}
|
|
|
|
NdisFreePacket(Packet);
|
|
}
|
|
|
|
|
|
//* InitForwardingPools - Initialize the packet and buffer pools used
|
|
// for forwarding operations.
|
|
//
|
|
// Returns: TRUE if the operations succeeded.
|
|
//
|
|
BOOLEAN InitForwardingPools()
|
|
{
|
|
NDIS_STATUS Status;
|
|
|
|
// Create our "large" forwarding buffer pool.
|
|
//
|
|
IpForwardLargePool = MdpCreatePool(BUFSIZE_LARGE_POOL, 'lfCT');
|
|
if (!IpForwardLargePool) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Create our "small" forwarding buffer pool.
|
|
//
|
|
IpForwardSmallPool = MdpCreatePool(BUFSIZE_SMALL_POOL, 'sfCT');
|
|
if (!IpForwardSmallPool) {
|
|
MdpDestroyPool(IpForwardLargePool);
|
|
IpForwardLargePool = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
// Create our forwarding packet pool.
|
|
//
|
|
NdisAllocatePacketPoolEx(&Status, &IpForwardPacketPool,
|
|
PACKET_POOL_SIZE, 0, sizeof(FWContext));
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
MdpDestroyPool(IpForwardSmallPool);
|
|
IpForwardSmallPool = NULL;
|
|
MdpDestroyPool(IpForwardLargePool);
|
|
IpForwardLargePool = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
NdisSetPacketPoolProtocolId(IpForwardPacketPool, NDIS_PROTOCOL_ID_TCP_IP);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* InitGateway - Initialize our gateway functionality.
|
|
//
|
|
// Called during init. time to initialize our gateway functionality. If we're
|
|
// not connfigured as a router, we do nothing. If we are, we allocate the
|
|
// resources we need and do other router initialization.
|
|
//
|
|
// Input: ci - Config info.
|
|
//
|
|
// Returns: TRUE if we succeed, FALSE if don't.
|
|
//
|
|
uint
|
|
InitGateway(IPConfigInfo * ci)
|
|
{
|
|
IPHeader *HeaderPtr = NULL;
|
|
uchar *FWBuffer = NULL;
|
|
RouteInterface *RtIF;
|
|
NetTableEntry *NTE;
|
|
uint i;
|
|
|
|
// If we're going to be a router, allocate and initialize the resources we
|
|
// need for that.
|
|
BCastRSQ = NULL;
|
|
|
|
RtPI = CTEAllocMemNBoot(sizeof(ProtInfo), 'JiCT');
|
|
if (RtPI == (ProtInfo *) NULL)
|
|
goto failure;
|
|
|
|
RtPI->pi_xmitdone = FWSendComplete;
|
|
|
|
for (i = 0; i < NET_TABLE_SIZE; i++) {
|
|
NetTableEntry *NetTableList = NewNetTableList[i];
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
RtIF = (RouteInterface *) NTE->nte_if;
|
|
|
|
RtIF->ri_q.rsq_qh.fq_next = &RtIF->ri_q.rsq_qh;
|
|
RtIF->ri_q.rsq_qh.fq_prev = &RtIF->ri_q.rsq_qh;
|
|
RtIF->ri_q.rsq_running = FALSE;
|
|
RtIF->ri_q.rsq_pending = 0;
|
|
RtIF->ri_q.rsq_qlength = 0;
|
|
CTEInitLock(&RtIF->ri_q.rsq_lock);
|
|
}
|
|
}
|
|
|
|
BCastRSQ = CTEAllocMemNBoot(sizeof(RouteSendQ), 'KiCT');
|
|
|
|
if (BCastRSQ == (RouteSendQ *) NULL)
|
|
goto failure;
|
|
|
|
BCastRSQ->rsq_qh.fq_next = &BCastRSQ->rsq_qh;
|
|
BCastRSQ->rsq_qh.fq_prev = &BCastRSQ->rsq_qh;
|
|
BCastRSQ->rsq_pending = 0;
|
|
BCastRSQ->rsq_maxpending = DEFAULT_MAX_PENDING;
|
|
BCastRSQ->rsq_qlength = 0;
|
|
BCastRSQ->rsq_running = FALSE;
|
|
CTEInitLock(&BCastRSQ->rsq_lock);
|
|
|
|
RtIF = (RouteInterface *) &LoopInterface;
|
|
RtIF->ri_q.rsq_maxpending = DEFAULT_MAX_PENDING;
|
|
|
|
if (!InitForwardingPools()) {
|
|
goto failure;
|
|
}
|
|
return TRUE;
|
|
|
|
failure:
|
|
if (RtPI != NULL)
|
|
CTEFreeMem(RtPI);
|
|
if (BCastRSQ != NULL)
|
|
CTEFreeMem(BCastRSQ);
|
|
if (HeaderPtr != NULL)
|
|
CTEFreeMem(HeaderPtr);
|
|
if (FWBuffer != NULL)
|
|
CTEFreeMem(FWBuffer);
|
|
|
|
ForwardBCast = FALSE;
|
|
ForwardPackets = FALSE;
|
|
RouterConfigured = FALSE;
|
|
IPEnableRouterRefCount = (ci->ici_gateway ? 1 : 0);
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
GetIFAndLink(void *Rce, ULONG * IFIndex, IPAddr * NextHop)
|
|
{
|
|
RouteTableEntry *RTE = NULL;
|
|
RouteCacheEntry *RCE = (RouteCacheEntry *) Rce;
|
|
Interface *IF;
|
|
KIRQL rtlIrql;
|
|
|
|
CTEGetLock(&RouteTableLock.Lock, &rtlIrql);
|
|
|
|
if (RCE && (RCE->rce_flags & RCE_VALID) &&
|
|
!(RCE->rce_flags & RCE_LINK_DELETED))
|
|
RTE = RCE->rce_rte;
|
|
|
|
if (RTE) {
|
|
|
|
if ((IF = IF_FROM_RTE(RTE)) == NULL) {
|
|
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
*IFIndex = IF->if_index;
|
|
if (RTE->rte_link) {
|
|
ASSERT(IF->if_flags & IF_FLAGS_P2MP);
|
|
*NextHop = RTE->rte_link->link_NextHop;
|
|
} else
|
|
*NextHop = NULL_IP_ADDR;
|
|
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
|
|
return IP_SUCCESS;
|
|
}
|
|
CTEFreeLock(&RouteTableLock.Lock, rtlIrql);
|
|
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
|
|
#pragma END_INIT
|
|
|