mirror of https://github.com/lianthony/NT4.0
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.
4703 lines
144 KiB
4703 lines
144 KiB
/********************************************************************/
|
|
/** Microsoft LAN Manager **/
|
|
/** Copyright(c) Microsoft Corp., 1990-1995 **/
|
|
/********************************************************************/
|
|
/* :ts=4 */
|
|
|
|
//*** iproute.c - IP routing routines.
|
|
//
|
|
// This file contains all the routines related to IP routing, including
|
|
// routing table lookup and management routines.
|
|
|
|
#include "oscfg.h"
|
|
#include "cxport.h"
|
|
#include "ndis.h"
|
|
#include "ip.h"
|
|
#include "ipdef.h"
|
|
#include "ipinit.h"
|
|
#include "info.h"
|
|
#include "tdistat.h"
|
|
#include "iproute.h"
|
|
#include "iprtdef.h"
|
|
#include "ipxmit.h"
|
|
#include "igmp.h"
|
|
#include "tdiinfo.h"
|
|
#include "ipfilter.h"
|
|
|
|
extern NetTableEntry *NetTableList; // Pointer to the net table.
|
|
extern NetTableEntry *DHCPNTE; // Pointer to NTE being DHCP'd.
|
|
|
|
extern NetTableEntry *LoopNTE; // Pointer to loopback NTE.
|
|
extern Interface LoopInterface; // Pointer to loopback interface.
|
|
|
|
extern AddrTypeCache ATCache[];
|
|
extern int ATCIndex;
|
|
|
|
extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong);
|
|
extern uchar ParseRcvdOptions(IPOptInfo *, OptIndex *);
|
|
extern void ULMTUNotify(IPAddr Dest, IPAddr Src, uchar Prot, void *Ptr,
|
|
uint NewMTU);
|
|
|
|
extern Interface *IFList;
|
|
extern Interface *FirstIF;
|
|
|
|
#define ROUTE_TABLE_SIZE 32 // Size of the route table.
|
|
DEFINE_LOCK_STRUCTURE(RouteTableLock)
|
|
|
|
#define FWPACKET_GROW_AMOUNT 20
|
|
|
|
#define FW_BUF_SIZE 256 // Size of a forwarding buffer.
|
|
|
|
#define FW_BUF_GROW_AMOUNT 30720 // Enough for 20 Ethernet packets.
|
|
|
|
#define NO_SR 0
|
|
|
|
RouteTableEntry *RouteTable[ROUTE_TABLE_SIZE];
|
|
|
|
DEFINE_LOCK_STRUCTURE(FWPacketFreeLock)
|
|
DEFINE_LOCK_STRUCTURE(FWBufFreeLock)
|
|
|
|
PNDIS_PACKET FWPacketFree; // Free list of forwarding packets.
|
|
PNDIS_BUFFER FWBufFree; // Free list of forwarding buffers.
|
|
|
|
uint MaxFWPackets; // Maximum number of forward packets allowed.
|
|
uint CurrentFWPackets; // Number of forwarding packets currently
|
|
// allocated.
|
|
uint MaxFWBufferSize; // Maximum number of forwarding buffers allowed.
|
|
uint CurrentFWBufferSize; // Number of forwarding buffers allocated.
|
|
|
|
uchar ForwardPackets; // Flag indicating whether we should forward.
|
|
uchar RouterConfigured; // TRUE if we were initially
|
|
// configured as a router.
|
|
uchar ForwardBCast; // Flag indicating if we should forward bcasts.
|
|
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)
|
|
};
|
|
|
|
CTETimer IPRouteTimer;
|
|
|
|
// Pointer to callout routine for dial on demand.
|
|
IPMapRouteToInterfacePtr DODCallout;
|
|
|
|
// Pointer to packet filter handler.
|
|
IPPacketFilterPtr ForwardFilterPtr;
|
|
|
|
RouteInterface DummyInterface; // Dummy interface.
|
|
|
|
#ifdef NT
|
|
#ifdef ALLOC_PRAGMA
|
|
//
|
|
// Make init code disposable.
|
|
//
|
|
int InitRouting(IPConfigInfo *ci);
|
|
|
|
#pragma alloc_text(INIT, InitRouting)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
#endif // NT
|
|
|
|
|
|
#define InsertAfterRTE(P, R) (R)->rte_next = (P)->rte_next;\
|
|
(P)->rte_next = (R)
|
|
|
|
#define InsertRTE(R) {\
|
|
RouteTableEntry *__P__; \
|
|
__P__ = FindInsertPoint((R)); \
|
|
InsertAfterRTE(__P__, (R)); \
|
|
}
|
|
|
|
#define RemoveRTE(P, R) (P)->rte_next = (R)->rte_next;
|
|
|
|
//** 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
|
|
DummyXmit(void *Context, PNDIS_PACKET Packet, IPAddr Dest, RouteCacheEntry *RCE)
|
|
{
|
|
DbgPrint("TCPIP: Dummy Xmit called - NOT GOOD\n");
|
|
CTEAssert(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
|
|
DummyXfer(void *Context, NDIS_HANDLE TDContext, uint Dummy, uint Offset, uint BytesToCopy,
|
|
PNDIS_PACKET DestPacket, uint *BytesCopied)
|
|
{
|
|
DbgPrint("TCPIP: DummyXfer called - NOT GOOD\n");
|
|
|
|
CTEAssert(FALSE);
|
|
|
|
return NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
//* DummyClose - Dummy close routine.
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// Entry: Context - Unused.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
DummyClose(void *Context)
|
|
{
|
|
DbgPrint("TCPIP: Dummy Close called - NOT GOOD\n");
|
|
|
|
CTEAssert(FALSE);
|
|
}
|
|
|
|
//* DummyInvalidate - .
|
|
//
|
|
// A dummy routine that should never be called.
|
|
//
|
|
// Entry: Context - Unused.
|
|
// RCE - Pointer to RCE to be invalidated.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
DummyInvalidate(void *Context, RouteCacheEntry *RCE)
|
|
{
|
|
DbgPrint("TCPIP: Dummy Invalidate called - NOT GOOD\n");
|
|
|
|
CTEAssert(FALSE);
|
|
|
|
}
|
|
|
|
//* 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
|
|
DummyQInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size,
|
|
void *Context)
|
|
{
|
|
DbgPrint("TCPIP: DummyQInfo called - NOT GOOD\n");
|
|
|
|
CTEAssert(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
|
|
DummySetInfo(void *IFContext, TDIObjectID *ID, void *Buffer, uint Size)
|
|
{
|
|
DbgPrint("TCPIP: DummySetInfo called - NOT GOOD\n");
|
|
|
|
CTEAssert(FALSE);
|
|
|
|
return TDI_INVALID_REQUEST;
|
|
}
|
|
|
|
//* DummyAddAddr - Dummy add address routine.
|
|
//
|
|
// Called at init time when we need to initialize ourselves.
|
|
//
|
|
uint
|
|
DummyAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2)
|
|
{
|
|
CTEAssert(FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* DummyDelAddr - Dummy del address routine.
|
|
//
|
|
// Called at init time when we need to initialize ourselves.
|
|
//
|
|
uint
|
|
DummyDelAddr(void *Context, uint Type, IPAddr Address, IPMask Mask)
|
|
{
|
|
DbgPrint("TCPIP: DummyAddAddr called - NOT GOOD\n");
|
|
|
|
CTEAssert(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
|
|
DummyGetEList(void *Context, TDIEntityID *EntityList, uint *Count)
|
|
{
|
|
DbgPrint("TCPIP: DummyGetEList called - NOT GOOD\n");
|
|
|
|
CTEAssert(FALSE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef _PNP_POWER
|
|
//* 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.
|
|
//
|
|
void
|
|
DerefIF(Interface *IF)
|
|
{
|
|
uint Original;
|
|
|
|
Original = CTEInterlockedAddUlong(
|
|
&IF->if_refcount,
|
|
(ULONG)-1,
|
|
&RouteTableLock
|
|
);
|
|
if (Original != 1) {
|
|
return;
|
|
} else {
|
|
// We just decremented the last reference. Wake whoever is
|
|
// blocked on it.
|
|
CTEAssert(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)
|
|
{
|
|
uint Original;
|
|
|
|
IF->if_refcount--;
|
|
|
|
if (IF->if_refcount != 0) {
|
|
return;
|
|
} else {
|
|
// We just decremented the last reference. Wake whoever is
|
|
// blocked on it.
|
|
CTEAssert(IF->if_block != NULL);
|
|
CTESignal(IF->if_block, NDIS_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//* GetHashMask - Get mask to use with address when hashing.
|
|
//
|
|
// Called when we need to decide on the mask to use when hashing. If the
|
|
// supplied mask is the host mask or the default mask, we'll use that. Else
|
|
// if the supplied mask is at least as specific as the net mask, we'll use the
|
|
// net mask. Otherwise we drop back to the default mask.
|
|
//
|
|
// Input: Destination - Destination we'll be hashing on.
|
|
// Mask - Caller supplied mask.
|
|
//
|
|
// Returns: Mask to use.
|
|
//
|
|
IPMask
|
|
GetHashMask(IPAddr Destination, IPMask Mask)
|
|
{
|
|
IPMask NetMask;
|
|
|
|
if (Mask == HOST_MASK || Mask == DEFAULT_MASK)
|
|
return Mask;
|
|
|
|
NetMask = IPNetMask(Destination);
|
|
|
|
if ((NetMask & Mask) == NetMask)
|
|
return NetMask;
|
|
|
|
return DEFAULT_MASK;
|
|
|
|
}
|
|
|
|
//** 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.
|
|
//
|
|
// Returns: The 'best match' NTE.
|
|
//
|
|
NetTableEntry *
|
|
BestNTEForIF(IPAddr Address, Interface *IF)
|
|
{
|
|
NetTableEntry *CurrentNTE, *FoundNTE;
|
|
|
|
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))
|
|
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) address.
|
|
for (CurrentNTE = NetTableList; CurrentNTE != NULL;
|
|
CurrentNTE = CurrentNTE->nte_next) {
|
|
if (CurrentNTE != LoopNTE && (CurrentNTE->nte_flags & NTE_VALID))
|
|
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;
|
|
|
|
BCastAddr = NTE->nte_if->if_bcast;
|
|
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
|
|
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.
|
|
|
|
if (IGMPLevel == 2) {
|
|
IGMPAddr *AddrPtr;
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&NTE->nte_lock, &Handle);
|
|
AddrPtr = NTE->nte_igmplist;
|
|
while (AddrPtr != NULL) {
|
|
if (IP_ADDR_EQUAL(Address, AddrPtr->iga_addr))
|
|
break;
|
|
else
|
|
AddrPtr = AddrPtr->iga_next;
|
|
}
|
|
|
|
CTEFreeLock(&NTE->nte_lock, Handle);
|
|
if (AddrPtr != NULL)
|
|
return DEST_MCAST;
|
|
}
|
|
}
|
|
}
|
|
|
|
// A global bcast is certainly a bcast on this net.
|
|
if (IP_ADDR_EQUAL(Address, BCastAddr))
|
|
return DEST_BCAST;
|
|
|
|
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.
|
|
uchar Result; // Result of broadcast check.
|
|
IPMask SNMask;
|
|
IPAddr MaskedAddress;
|
|
IPAddr LocalAddress;
|
|
|
|
|
|
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.
|
|
NTE = NetTableList;
|
|
do {
|
|
|
|
LocalAddress = NTE->nte_addr;
|
|
|
|
if ((NTE->nte_flags & NTE_VALID) &&
|
|
!IP_LOOPBACK(LocalAddress)) {
|
|
|
|
Mask = NTE->nte_mask;
|
|
MaskedAddress = LocalAddress & Mask;
|
|
|
|
if (IP_ADDR_EQUAL(Address, MaskedAddress) ||
|
|
IP_ADDR_EQUAL(Address,
|
|
(MaskedAddress |
|
|
(NTE->nte_if->if_bcast & ~Mask)))) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
NTE = NTE->nte_next;
|
|
} while (NTE != NULL);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//** FlushATCache - Flush an address from the ATCache
|
|
//
|
|
// This function takes an input address, and removes it from the ATCache,
|
|
// if it is present.
|
|
//
|
|
// Input: Address - Address to be check.
|
|
//
|
|
// Returns: Destination type.
|
|
//
|
|
void
|
|
FlushATCache(IPAddr Address)
|
|
{
|
|
uint i;
|
|
|
|
|
|
for (i=0; i<ATC_SIZE; i++) {
|
|
if (ATCache[i].atc_flags & (ATCache[i].atc_addr == Address)) {
|
|
ATCache[i].atc_flags = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//** GetAddrType - Return the type of a specified address.
|
|
//
|
|
// This function takes an input address, and determines what type it is. An
|
|
// address can be local, bcast, remote, or remote bcast.
|
|
//
|
|
// Input: Address - Address to be check.
|
|
//
|
|
// Returns: Destination type.
|
|
//
|
|
uchar
|
|
GetAddrType(IPAddr Address)
|
|
{
|
|
NetTableEntry *NTE; // Pointer to current NTE.
|
|
IPMask Mask; // Mask for address.
|
|
uchar Result; // Result of broadcast check.
|
|
IPMask SNMask;
|
|
uint saveATCIndex;
|
|
uint i;
|
|
|
|
saveATCIndex = ATCIndex & ATC_MASK;
|
|
i = saveATCIndex;
|
|
|
|
do {
|
|
if (ATCache[i].atc_flags && (ATCache[i].atc_addr == Address)) {
|
|
Result = ATCache[i].atc_type;
|
|
if (ATCache[i].atc_flags && (ATCache[i].atc_addr == Address)) {
|
|
return(Result);
|
|
}
|
|
}
|
|
i = (--i) & ATC_MASK;
|
|
} while (i != saveATCIndex );
|
|
|
|
|
|
|
|
if (!CLASSE_ADDR(Address)) {
|
|
// See if it's one of our local addresses, or a broadcast
|
|
// on a local address.
|
|
NTE = NetTableList;
|
|
do {
|
|
|
|
if (IP_ADDR_EQUAL(NTE->nte_addr, Address) &&
|
|
(NTE->nte_flags & NTE_VALID)) {
|
|
Result = DEST_LOCAL;
|
|
goto gat_exit;
|
|
}
|
|
|
|
if ((Result = IsBCastOnNTE(Address, NTE)) != DEST_LOCAL) {
|
|
goto gat_exit;
|
|
}
|
|
|
|
// See if the destination has a valid host part.
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
NTE = NTE->nte_next;
|
|
} while (NTE != NULL);
|
|
|
|
// 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 it,
|
|
// 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, Address & Mask) ||
|
|
IP_ADDR_EQUAL(Address, NULL_IP_ADDR)) {
|
|
Result = DEST_INVALID;
|
|
goto gat_exit;
|
|
}
|
|
|
|
// Must be remote.
|
|
Result = DEST_REMOTE;
|
|
goto gat_exit;
|
|
}
|
|
|
|
Result = DEST_INVALID;
|
|
|
|
gat_exit:
|
|
|
|
++ATCIndex;
|
|
|
|
i = ATCIndex & ATC_MASK;
|
|
|
|
ATCache[i].atc_addr = Address;
|
|
ATCache[i].atc_type = Result;
|
|
ATCache[i].atc_flags = 1;
|
|
return(Result);
|
|
|
|
}
|
|
|
|
//** IPHash - IP hash function.
|
|
//
|
|
// This is the function to compute the hash index from a masked address.
|
|
//
|
|
// Input: Address - Masked address to be hashed.
|
|
//
|
|
// Returns: Hashed value.
|
|
//
|
|
uint
|
|
IPHash(IPAddr Address)
|
|
{
|
|
uchar *i = (uchar *)&Address;
|
|
return (i[0] + i[1] + i[2] + i[3]) & (ROUTE_TABLE_SIZE-1);
|
|
}
|
|
|
|
//** 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;
|
|
int 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;
|
|
|
|
// 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;
|
|
LocalNTE = NetTableList;
|
|
do {
|
|
if (LocalNTE != OriginalNTE) {
|
|
if (IP_ADDR_EQUAL(Address, LocalNTE->nte_addr) &&
|
|
(LocalNTE->nte_flags & NTE_VALID)) {
|
|
*NTE = LocalNTE;
|
|
return DEST_LOCAL; // For us, just return.
|
|
}
|
|
|
|
// 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;
|
|
|
|
} while (LocalNTE != NULL);
|
|
|
|
// 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 (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 (DHCPNTE != NULL && DHCPNTE == *NTE) {
|
|
return DEST_LOCAL;
|
|
}
|
|
|
|
return DEST_REMOTE;
|
|
}
|
|
|
|
|
|
//** FindSpecificRTE - Look for a particular RTE.
|
|
//
|
|
// Called when we're adding a route and want to find a particular RTE.
|
|
// We take in the destination, mask, first hop, and src addr, and search
|
|
// the appropriate routeing table chain. We assume the caller has the
|
|
// RouteTableLock held. If we find the match, we'll return a pointer to the
|
|
// RTE with the RTE lock held, as well as a pointer to the previous RTE in
|
|
// the chain.
|
|
//
|
|
// Input: Dest - Destination to search for.
|
|
// Mask - Mask for destination.
|
|
// FirstHop - FirstHop to Dest.
|
|
// OutIF - Pointer to outgoing interface structure.
|
|
// PrevRTE - Place to put PrevRTE, if found.
|
|
//
|
|
// Returns: Pointer to matching RTE if found, or NULL if not.
|
|
//
|
|
RouteTableEntry *
|
|
FindSpecificRTE(IPAddr Dest, IPMask Mask, IPAddr FirstHop, Interface *OutIF,
|
|
RouteTableEntry **PrevRTE)
|
|
{
|
|
uint Index;
|
|
IPMask HashMask;
|
|
RouteTableEntry *TempRTE, *CurrentRTE;
|
|
|
|
HashMask = GetHashMask(Dest, Mask);
|
|
|
|
Index = IPHash(Dest & HashMask);
|
|
TempRTE = STRUCT_OF(RouteTableEntry, &RouteTable[Index], rte_next);
|
|
CurrentRTE = TempRTE->rte_next;
|
|
|
|
//
|
|
// If this has been called because user mode was trying to set the route
|
|
// to INVALID, then the OUTIF will be DummyInterface, but we want to match
|
|
// any interface, since the IF will have already been plumbed by DODCallOut
|
|
//
|
|
|
|
if(OutIF == (Interface *)&DummyInterface)
|
|
{
|
|
//
|
|
// Match everything but the interface
|
|
//
|
|
|
|
while (CurrentRTE != NULL)
|
|
{
|
|
if (IP_ADDR_EQUAL(CurrentRTE->rte_dest,Dest) &&
|
|
CurrentRTE->rte_mask == Mask &&
|
|
IP_ADDR_EQUAL(CurrentRTE->rte_addr, FirstHop))
|
|
{
|
|
break;
|
|
}
|
|
|
|
TempRTE = CurrentRTE;
|
|
CurrentRTE = CurrentRTE->rte_next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Walk the table, looking for a match.
|
|
while (CurrentRTE != NULL) {
|
|
// See if everything matches.
|
|
if (IP_ADDR_EQUAL(CurrentRTE->rte_dest,Dest) &&
|
|
CurrentRTE->rte_mask == Mask &&
|
|
IP_ADDR_EQUAL(CurrentRTE->rte_addr, FirstHop) &&
|
|
CurrentRTE->rte_if == OutIF)
|
|
break;
|
|
|
|
TempRTE = CurrentRTE;
|
|
CurrentRTE = CurrentRTE->rte_next;
|
|
}
|
|
}
|
|
|
|
*PrevRTE = TempRTE;
|
|
return CurrentRTE;
|
|
|
|
}
|
|
|
|
//** 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);
|
|
|
|
if (RTE == NULL)
|
|
return(TRUE);
|
|
|
|
if (RTE->rte_proto == IRE_PROTO_ICMP) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
//** FindRTE - Find a matching RTE in a hash table chain.
|
|
//
|
|
// Called when we want to find a matching RTE. We take in a destination,
|
|
// a source, a hash index, and a maximum priority, and walk down the
|
|
// chain specified by the index looking for a matching RTE. If we can find
|
|
// one, we'll keep looking hoping for a match on the source address.
|
|
//
|
|
// The caller must hold the RouteTableLock before calling this function.
|
|
//
|
|
// Input: Dest - Destination we're trying to reach.
|
|
// Source - Source address to match.
|
|
// Index - Index of chain to search.
|
|
// MaxPri - Maximum acceptable priority.
|
|
// MinPri - Minimum acceptable priority.
|
|
//
|
|
// Returns: Pointer to RTE if found, or NULL if not.
|
|
//
|
|
RouteTableEntry *
|
|
FindRTE(IPAddr Dest, IPAddr Source, uint Index, uint MaxPri, uint MinPri)
|
|
{
|
|
RouteTableEntry *CurrentRTE;
|
|
uint RTEPri;
|
|
uint Metric;
|
|
RouteTableEntry *FoundRTE;
|
|
|
|
// First walk down the chain, skipping those RTEs that have a
|
|
// a priority greater than what we want.
|
|
|
|
CurrentRTE = RouteTable[Index];
|
|
|
|
for (;;) {
|
|
if (CurrentRTE == NULL)
|
|
return NULL; // Hit end of chain, bounce out.
|
|
|
|
if (CurrentRTE->rte_priority <= MaxPri)
|
|
break; // He's a possible match.
|
|
|
|
// Priority is too big. Try the next one.
|
|
CurrentRTE = CurrentRTE->rte_next;
|
|
}
|
|
|
|
FoundRTE = NULL;
|
|
|
|
// When we get here, we have a locked RTE with a priority less than or
|
|
// equal to what was specifed.
|
|
// Examine it, and if it doesn't match try the next one. If it does match
|
|
// we'll stash it temporarily and keep looking for one that matches the
|
|
// specifed source.
|
|
for (;;) {
|
|
|
|
// The invariant at the top of this loop is that CurrentRTE points to
|
|
// a candidate RTE, locked with the handle in CurrentHandle.
|
|
|
|
if (CurrentRTE->rte_flags & RTE_VALID) {
|
|
// He's valid. Make sure he's at least the priority we need. If
|
|
// he is, see if he matches. Otherwise we're done.
|
|
|
|
if (CurrentRTE->rte_priority < MinPri) {
|
|
// His priority is too small. Since the list is in sorted order,
|
|
// all following routes must have too low a priority, so we're
|
|
// done.
|
|
return NULL;
|
|
}
|
|
|
|
if (IP_ADDR_EQUAL(Dest & CurrentRTE->rte_mask, CurrentRTE->rte_dest)) {
|
|
// He's valid for this route. Save the current information,
|
|
// and look for a matching source.
|
|
FoundRTE = CurrentRTE;
|
|
|
|
if (!IP_ADDR_EQUAL(Source, NULL_IP_ADDR) &&
|
|
!AddrOnIF(CurrentRTE->rte_if, Source)) {
|
|
RTEPri = CurrentRTE->rte_priority;
|
|
Metric = CurrentRTE->rte_metric;
|
|
|
|
CurrentRTE = CurrentRTE->rte_next;
|
|
|
|
// We've save the info. Starting at the next RTE, look for
|
|
// an RTE that matches both the mask criteria and the source
|
|
// address. The search will terminate when we hit the end
|
|
// of the list, or the RTE we're examing has a different
|
|
// (presumably lesser) priority (or greater metric), or we
|
|
// find a match.
|
|
while (CurrentRTE != NULL &&
|
|
CurrentRTE->rte_priority == RTEPri &&
|
|
CurrentRTE->rte_metric == Metric) {
|
|
|
|
|
|
// Skip invalid route types.
|
|
if (CurrentRTE->rte_flags & RTE_VALID) {
|
|
if (IP_ADDR_EQUAL(Dest & CurrentRTE->rte_mask,
|
|
CurrentRTE->rte_dest)) {
|
|
if (AddrOnIF(CurrentRTE->rte_if, Source)) {
|
|
// He matches the source. Free the old lock,
|
|
// and break out.
|
|
FoundRTE = CurrentRTE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
CurrentRTE = CurrentRTE->rte_next;
|
|
}
|
|
}
|
|
|
|
// At this point, FoundRTE points to the RTE we want to return,
|
|
// and *Handle has the lock handle for the RTE. Break out.
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
CurrentRTE = CurrentRTE->rte_next;
|
|
|
|
if (CurrentRTE != NULL) {
|
|
continue;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
return FoundRTE;
|
|
}
|
|
|
|
//* 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 = RouteTable[IPHash(0)];
|
|
|
|
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->rte_next;
|
|
}
|
|
|
|
DefGWActive += Count;
|
|
return Count;
|
|
}
|
|
|
|
//* 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) {
|
|
CTEAssert(CurrentRCE->rce_rte == RTE);
|
|
CurrentRCE->rce_flags &= ~RCE_VALID;
|
|
CurrentRCE->rce_rte = (RouteTableEntry *)OutIF;
|
|
if ((CurrentRCE->rce_flags & RCE_CONNECTED) &&
|
|
CurrentRCE->rce_usecnt == 0) {
|
|
|
|
(*(OutIF->if_invalidate))(OutIF->if_lcontext, CurrentRCE);
|
|
#ifdef _PNP_POWER
|
|
if (CurrentRCE->rce_flags & RCE_REFERENCED) {
|
|
LockedDerefIF(OutIF);
|
|
CurrentRCE->rce_flags &= ~RCE_REFERENCED;
|
|
}
|
|
#endif
|
|
}
|
|
} else
|
|
CTEAssert(FALSE);
|
|
|
|
TempRCE = CurrentRCE->rce_next;
|
|
CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle);
|
|
CurrentRCE = TempRCE;
|
|
}
|
|
|
|
}
|
|
|
|
//** 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.
|
|
//
|
|
// 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)
|
|
{
|
|
uint NewIFIndex;
|
|
Interface *NewIF;
|
|
NetTableEntry *NewNTE;
|
|
|
|
if (DODCallout != NULL) {
|
|
// There is a callout. See if it can help us.
|
|
NewIFIndex = (*DODCallout)(RTE->rte_context, Destination, Source,
|
|
Protocol, Buffer, Length);
|
|
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) {
|
|
// 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;
|
|
RTE->rte_mtu = NewIF->if_mtu - sizeof(IPHeader);
|
|
return RTE;
|
|
} else
|
|
CTEAssert(FALSE);
|
|
}
|
|
}
|
|
|
|
// Either the callout is NULL, or the callout couldn't map a inteface index.
|
|
return NULL;
|
|
}
|
|
|
|
//** LookupRTE - Lookup a routing table entry.
|
|
//
|
|
// This routine looks up a routing table entry, and returns with the entry
|
|
// locked if it finds one. If it doesn't find one, it returns NULL. This
|
|
// routine assumes that the routing table is locked when it is called.
|
|
//
|
|
// The routeing table is organized as an open hash table. The table contains
|
|
// routes to hosts, subnets, and nets. Host routes are hashed on the host
|
|
// address, other non-default routes on the destination anded with the net
|
|
// mask, and default routes wind up in bucket 0. Within each bucket chain
|
|
// the routes are sorted with the greatest priority (i.e. number of bits in the
|
|
// route mask) first, and within each priority class the routes are sorted
|
|
// with the lowest metric first. The caller may specify a maximum priority
|
|
// for the route to be found. We look for routes in order of most specific to
|
|
// least specifc, i.e. first host routes, then other non-default routes, and
|
|
// finally default routes. We give preference to routes that are going out
|
|
// on an interface with an address that matches the input source address.
|
|
//
|
|
// It might be worthile in the future to split this up into multiple tables,
|
|
// so that we have a table for host routes, a table for non-default routes,
|
|
// and a list of default routes.
|
|
//
|
|
// Entry: Address - Address for which a route is to be found.
|
|
// Src - IPAddr of source (may be 0).
|
|
// MaxPri - Maximum priority of route to find.
|
|
//
|
|
// Returns: A pointer to the locked RTE if we find one, or NULL if we don't
|
|
//
|
|
RouteTableEntry *
|
|
LookupRTE(IPAddr Address, IPAddr Src, uint MaxPri)
|
|
{
|
|
RouteTableEntry *RTE;
|
|
|
|
// First try to find a host route, if we're allowed to.
|
|
if (MaxPri == HOST_ROUTE_PRI) {
|
|
RTE = FindRTE(Address, Src, IPHash(Address), MaxPri, MaxPri);
|
|
if (RTE != NULL) {
|
|
return RTE;
|
|
}
|
|
}
|
|
|
|
// Don't have or weren't allowed to find a host route. See if we can
|
|
// find a non-default route.
|
|
if (MaxPri > DEFAULT_ROUTE_PRI) {
|
|
RTE = FindRTE(Address, Src, IPHash(Address & IPNetMask(Address)),
|
|
MaxPri, DEFAULT_ROUTE_PRI + 1);
|
|
if (RTE != NULL) {
|
|
return RTE;
|
|
}
|
|
}
|
|
|
|
// No non-default route. Try a default route.
|
|
RTE = FindRTE(Address, Src, IPHash(0), MaxPri, DEFAULT_ROUTE_PRI);
|
|
|
|
return RTE;
|
|
|
|
}
|
|
|
|
//** 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 NULL.
|
|
//
|
|
void *
|
|
GetRouteContext(IPAddr Destination, IPAddr Source)
|
|
{
|
|
CTELockHandle Handle;
|
|
RouteTableEntry *RTE;
|
|
ROUTE_CONTEXT Context;
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
RTE = LookupRTE(Destination, Source, HOST_ROUTE_PRI);
|
|
if (RTE != NULL) {
|
|
Context = RTE->rte_context;
|
|
} else
|
|
Context = NULL;
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
|
|
return(Context);
|
|
}
|
|
|
|
//* FindInsertPoint - Find out where to insert an RTE in the table.
|
|
//
|
|
// Called to find out where to insert an RTE. We hash into the table,
|
|
// and walk the chain until we hit the end or we find an RTE with a
|
|
// lesser priority or a greater metric. Once we find the appropriate spot
|
|
// we return a pointer to the RTE immediately prior to the one we want to
|
|
// insert. We assume the caller holds the lock on the route table when calling
|
|
// this function.
|
|
//
|
|
// Input: InsertRTE - RTE we're going to (eventually) insert.
|
|
//
|
|
// Returns: Pointer to RTE in insert after.
|
|
//
|
|
RouteTableEntry *
|
|
FindInsertPoint(RouteTableEntry *InsertRTE)
|
|
{
|
|
RouteTableEntry *PrevRTE, *CurrentRTE;
|
|
IPMask HashMask, Mask;
|
|
uint Priority, Metric;
|
|
uint Index;
|
|
|
|
Priority = InsertRTE->rte_priority;
|
|
Metric = InsertRTE->rte_metric;
|
|
|
|
// First figure out where he should go. We'll hash on the whole address
|
|
// if the mask allows us to, or on the net portion if this is a non-default
|
|
// route.
|
|
|
|
Mask = InsertRTE->rte_mask;
|
|
HashMask = GetHashMask(InsertRTE->rte_dest, Mask);
|
|
|
|
Index = IPHash(InsertRTE->rte_dest & HashMask);
|
|
|
|
PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[Index], rte_next);
|
|
CurrentRTE = PrevRTE->rte_next;
|
|
|
|
// Walk the table, looking for a place to insert it.
|
|
while (CurrentRTE != NULL) {
|
|
if (CurrentRTE->rte_priority < Priority) {
|
|
break;
|
|
}
|
|
|
|
if (CurrentRTE->rte_priority == Priority) {
|
|
// Priorities match. Check the metrics.
|
|
if (CurrentRTE->rte_metric > Metric) {
|
|
// Our metric is smaller than his, so we're done.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Either his priority is greater than ours or his metric is less
|
|
// than or equal to ours. Check the next one.
|
|
PrevRTE = CurrentRTE;
|
|
CurrentRTE = CurrentRTE->rte_next;
|
|
}
|
|
|
|
// At this point, we've either found the correct spot or hit the end
|
|
// of the list.
|
|
return PrevRTE;
|
|
|
|
}
|
|
|
|
|
|
//** 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, &TableLock);
|
|
Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI);
|
|
|
|
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;
|
|
|
|
*MTU = Route->rte_mtu;
|
|
#ifdef _PNP_POWER
|
|
IF->if_refcount++;
|
|
#endif
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return IF;
|
|
} else { // Couldn't find a route.
|
|
CTEFreeLock(&RouteTableLock, 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.
|
|
//
|
|
// 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)
|
|
{
|
|
CTELockHandle TableLock; // Lock handle for routing table.
|
|
RouteTableEntry *Route; // Pointer to route table entry for route.
|
|
Interface *IF;
|
|
|
|
CTEGetLock(&RouteTableLock, &TableLock);
|
|
Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI);
|
|
|
|
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;
|
|
|
|
|
|
// 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);
|
|
if (Route == NULL) {
|
|
// Couldn't bring it up.
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return NULL;
|
|
} else
|
|
IF = Route->rte_if;
|
|
} else
|
|
IF = Route->rte_if;
|
|
|
|
*MTU = Route->rte_mtu;
|
|
#ifdef _PNP_POWER
|
|
IF->if_refcount++;
|
|
#endif
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return IF;
|
|
} else { // Couldn't find a route.
|
|
CTEFreeLock(&RouteTableLock, 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)
|
|
{
|
|
RouteEntryContext *REContext = (RouteEntryContext *)Context;
|
|
IPRouteEntry *IPREntry = (IPRouteEntry *)Buffer;
|
|
RouteTableEntry *CurrentRTE;
|
|
uint i;
|
|
uint Now = CTESystemUpTime() / 1000L;
|
|
Interface *IF;
|
|
NetTableEntry *SrcNTE;
|
|
|
|
CurrentRTE = REContext->rec_rte;
|
|
|
|
// 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;
|
|
if (IP_ADDR_EQUAL(CurrentRTE->rte_addr, IPADDR_LOCAL)) {
|
|
SrcNTE = BestNTEForIF(CurrentRTE->rte_dest, IF);
|
|
if (IF->if_nte != NULL && SrcNTE != NULL)
|
|
IPREntry->ire_nexthop = SrcNTE->nte_addr;
|
|
else
|
|
IPREntry->ire_nexthop = IPREntry->ire_dest;
|
|
} else {
|
|
IPREntry->ire_nexthop = CurrentRTE->rte_addr;
|
|
}
|
|
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;
|
|
|
|
// We've filled it in. Now update the context.
|
|
if (CurrentRTE->rte_next != NULL) {
|
|
REContext->rec_rte = CurrentRTE->rte_next;
|
|
return TRUE;
|
|
} else {
|
|
// The next RTE is NULL. Loop through the RouteTable looking for a new
|
|
// one.
|
|
i = REContext->rec_index + 1;
|
|
while (i < ROUTE_TABLE_SIZE) {
|
|
if (RouteTable[i] != NULL) {
|
|
REContext->rec_rte = RouteTable[i];
|
|
REContext->rec_index = i;
|
|
return TRUE;
|
|
break;
|
|
} else
|
|
i++;
|
|
}
|
|
|
|
REContext->rec_index = 0;
|
|
REContext->rec_rte = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
//* RTValidateContext - Validate the context for reading the route table.
|
|
//
|
|
// Called to start reading the route table sequentially. We take in
|
|
// a context, and if the values are 0 we return information about the
|
|
// first route in the table. Otherwise we make sure that the context value
|
|
// is valid, and if it is we return TRUE.
|
|
// We assume the caller holds the route table lock.
|
|
//
|
|
// Input: Context - Pointer to a RouteEntryContext.
|
|
// Valid - Where to return information about context being
|
|
// valid.
|
|
//
|
|
// Returns: TRUE if more data to be read in table, FALSE if not. *Valid set
|
|
// to TRUE if input context is valid
|
|
//
|
|
uint
|
|
RTValidateContext(void *Context, uint *Valid)
|
|
{
|
|
RouteEntryContext *REContext = (RouteEntryContext *)Context;
|
|
uint i;
|
|
RouteTableEntry *TargetRTE;
|
|
RouteTableEntry *CurrentRTE;
|
|
|
|
i = REContext->rec_index;
|
|
TargetRTE = REContext->rec_rte;
|
|
|
|
// If the context values are 0 and NULL, we're starting from the beginning.
|
|
if (i == 0 && TargetRTE == NULL) {
|
|
*Valid = TRUE;
|
|
do {
|
|
if ((CurrentRTE = RouteTable[i]) != NULL) {
|
|
break;
|
|
}
|
|
i++;
|
|
} while (i < ROUTE_TABLE_SIZE);
|
|
|
|
if (CurrentRTE != NULL) {
|
|
REContext->rec_index = i;
|
|
REContext->rec_rte = CurrentRTE;
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
|
|
} else {
|
|
|
|
// We've been given a context. We just need to make sure that it's
|
|
// valid.
|
|
|
|
if (i < ROUTE_TABLE_SIZE) {
|
|
CurrentRTE = RouteTable[i];
|
|
while (CurrentRTE != NULL) {
|
|
if (CurrentRTE == TargetRTE) {
|
|
*Valid = TRUE;
|
|
return TRUE;
|
|
break;
|
|
} else {
|
|
CurrentRTE = CurrentRTE->rte_next;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// If we get here, we didn't find the matching RTE.
|
|
*Valid = FALSE;
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//* 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)
|
|
{
|
|
PrevRTE->rte_next = RTE->rte_next; // Splice him from the table.
|
|
IPSInfo.ipsi_numroutes--;
|
|
|
|
if (RTE->rte_mask == DEFAULT_MASK) {
|
|
// We're deleting a default route.
|
|
DefGWConfigured--;
|
|
if (RTE->rte_flags & RTE_VALID)
|
|
DefGWActive--;
|
|
if (DefGWActive == 0)
|
|
ValidateDefaultGWs(NULL_IP_ADDR);
|
|
}
|
|
|
|
InvalidateRCEChain(RTE);
|
|
// Free the old route.
|
|
CTEFreeMem(RTE);
|
|
|
|
}
|
|
|
|
//* DeleteRTEOnIF - 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
|
|
DeleteRTEOnIF(RouteTableEntry *RTE, void *Context, void *Context1)
|
|
{
|
|
Interface *IF = (Interface *)Context;
|
|
|
|
if (RTE->rte_if == IF && !IP_ADDR_EQUAL(RTE->rte_dest, IF->if_bcast))
|
|
return FALSE;
|
|
else
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
//* 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)
|
|
{
|
|
uint i;
|
|
CTELockHandle Handle;
|
|
RouteTableEntry *RTE, *PrevRTE;
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
for (i = 0; i < ROUTE_TABLE_SIZE; i++) {
|
|
|
|
PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], rte_next);
|
|
RTE = RouteTable[i];
|
|
while (RTE != NULL) {
|
|
if (!(*CallFunc)(RTE, Context, Context1)) {
|
|
DeleteRTE(PrevRTE, RTE);
|
|
} else {
|
|
PrevRTE = RTE;
|
|
}
|
|
RTE = PrevRTE->rte_next;
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
}
|
|
|
|
//** 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;
|
|
|
|
|
|
CTEGetLock(&RouteTableLock, &TableHandle);
|
|
|
|
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, TableHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
RTE = LookupRTE(RCE->rce_dest, RCE->rce_src, HOST_ROUTE_PRI);
|
|
|
|
// See if we found an RTE.
|
|
if (RTE != NULL) {
|
|
|
|
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)) {
|
|
Interface *IF;
|
|
// 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) {
|
|
IF = (Interface *)RCE->rce_rte;
|
|
(*(IF->if_invalidate))(IF->if_lcontext, RCE);
|
|
#ifdef _PNP_POWER
|
|
if (RCE->rce_flags & RCE_REFERENCED) {
|
|
LockedDerefIF(IF);
|
|
RCE->rce_flags &= ~RCE_REFERENCED;
|
|
}
|
|
#endif
|
|
} else {
|
|
CTEAssert(!(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;
|
|
|
|
// 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);
|
|
if (RTE != NULL) {
|
|
// Got one, so mark as connected.
|
|
CTEAssert(!(RCE->rce_flags & RCE_REFERENCED));
|
|
#ifdef _PNP_POWER
|
|
RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED);
|
|
RTE->rte_if->if_refcount++;
|
|
#else
|
|
RCE->rce_flags |= RCE_CONNECTED;
|
|
|
|
#endif
|
|
} else {
|
|
|
|
// Couldn't get a valid i/f. Mark the RCE as not connected,
|
|
// and set up to fail this call.
|
|
CTEAssert(FALSE);
|
|
RCE->rce_flags &= ~RCE_CONNECTED;
|
|
Status = FALSE;
|
|
}
|
|
} else {
|
|
// The RTE is connected, mark the RCE as connected.
|
|
CTEAssert(!(RCE->rce_flags & RCE_REFERENCED));
|
|
#ifdef _PNP_POWER
|
|
RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED);
|
|
RTE->rte_if->if_refcount++;
|
|
#else
|
|
RCE->rce_flags |= RCE_CONNECTED;
|
|
#endif
|
|
}
|
|
} 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);
|
|
if (RTE != NULL) {
|
|
RCE->rce_flags |= RCE_CONNECTED;
|
|
#ifdef _PNP_POWER
|
|
CTEAssert(!(RCE->rce_flags & RCE_REFERENCED));
|
|
RCE->rce_flags |= RCE_REFERENCED;
|
|
RTE->rte_if->if_refcount++;
|
|
#endif
|
|
} else {
|
|
|
|
// Couldn't connect, so fail.
|
|
CTEAssert(FALSE);
|
|
Status = FALSE;
|
|
}
|
|
} else { // Already connected, just mark as valid.
|
|
RCE->rce_flags |= RCE_CONNECTED;
|
|
#ifdef _PNP_POWER
|
|
if (!(RCE->rce_flags & RCE_REFERENCED)) {
|
|
RCE->rce_flags |= RCE_REFERENCED;
|
|
RTE->rte_if->if_refcount++;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Free the locks and we're done.
|
|
CTEFreeLock(&RCE->rce_lock, RCEHandle);
|
|
CTEFreeLock(&RouteTableLock, TableHandle);
|
|
return Status;
|
|
} else {
|
|
// No route! Fail the call.
|
|
CTEFreeLock(&RouteTableLock, TableHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
//** 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).
|
|
//
|
|
// Returns: Status of attempt to get new MTU.
|
|
//
|
|
IP_STATUS
|
|
IPGetPInfo(IPAddr Dest, IPAddr Src, uint *NewMTU, uint *MaxPathSpeed)
|
|
{
|
|
CTELockHandle Handle;
|
|
RouteTableEntry *RTE;
|
|
IP_STATUS Status;
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI);
|
|
if (RTE != NULL) {
|
|
if (NewMTU != NULL)
|
|
*NewMTU = RTE->rte_mtu;
|
|
if (MaxPathSpeed != NULL)
|
|
*MaxPathSpeed = RTE->rte_if->if_speed;
|
|
Status = IP_SUCCESS;
|
|
} else
|
|
Status = IP_DEST_HOST_UNREACHABLE;
|
|
|
|
CTEFreeLock(&RouteTableLock, 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.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
IPCheckRoute(IPAddr Dest, IPAddr Src)
|
|
{
|
|
RouteTableEntry *RTE;
|
|
RouteTableEntry *NewRTE;
|
|
RouteTableEntry *TempRTE;
|
|
CTELockHandle Handle;
|
|
uint Now = CTESystemUpTime() / 1000L;
|
|
|
|
if (DeadGWDetect) {
|
|
// We are doing dead G/W detection. Get the lock, and try and
|
|
// find the route.
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI);
|
|
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's not 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);
|
|
|
|
if (NewRTE == NULL) {
|
|
// Can't get there any other way so leave this
|
|
// one alone.
|
|
RTE->rte_flags |= RTE_VALID;
|
|
} else {
|
|
// There is another route, so destroy this one. Use
|
|
// FindSpecificRTE to find the previous RTE.
|
|
TempRTE = FindSpecificRTE(RTE->rte_dest, RTE->rte_mask,
|
|
RTE->rte_addr, RTE->rte_if, &NewRTE);
|
|
CTEAssert(TempRTE == RTE);
|
|
DeleteRTE(NewRTE, TempRTE);
|
|
}
|
|
} 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.
|
|
|
|
InvalidateRCEChain(RTE);
|
|
if (DefGWActive == 1) {
|
|
// No more active. Revalidate all of them,
|
|
// and try again.
|
|
ValidateDefaultGWs(NULL_IP_ADDR);
|
|
CTEAssert(DefGWActive == DefGWConfigured);
|
|
} else {
|
|
// More than one active, so invalidate this
|
|
// one, and move to the next one. Stamp the
|
|
// next one with a valid time of Now, so we
|
|
// don't move from him too easily.
|
|
--DefGWActive;
|
|
RTE->rte_flags &= ~RTE_VALID;
|
|
RTE = FindRTE(Dest, Src, IPHash(0),
|
|
DEFAULT_ROUTE_PRI, DEFAULT_ROUTE_PRI);
|
|
if (RTE == NULL) {
|
|
// No more default gateways! This is bad.
|
|
CTEAssert(FALSE);
|
|
ValidateDefaultGWs(NULL_IP_ADDR);
|
|
CTEAssert(DefGWActive == DefGWConfigured);
|
|
} else {
|
|
CTEAssert(RTE->rte_mask == DEFAULT_MASK);
|
|
RTE->rte_valid = Now;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CTEFreeLock(&RouteTableLock, 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;
|
|
|
|
CTEAssert(!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 - Source address we'll be using.
|
|
// 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;
|
|
|
|
|
|
if (!IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR))
|
|
Address = OptInfo->ioi_addr;
|
|
|
|
CTEGetLock(&RouteTableLock, &TableLock);
|
|
|
|
// Make sure we're not in DHCP update.
|
|
if (DHCPActivityCount != 0) {
|
|
// We are updating DHCP. Just fail this now, since we're in an
|
|
// indeterminate state.
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
|
|
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;
|
|
|
|
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, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
}
|
|
|
|
// Find the route for this guy. If we can't find one, return NULL.
|
|
RTE = LookupRTE(Address, Src, HOST_ROUTE_PRI);
|
|
|
|
if (RTE != (RouteTableEntry *)NULL) {
|
|
CTELockHandle RCEHandle;
|
|
RouteCacheEntry *OldRCE;
|
|
|
|
// We found one.
|
|
*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, 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;
|
|
else {
|
|
NetTableEntry *SrcNTE;
|
|
|
|
SrcNTE = BestNTEForIF(
|
|
ADDR_FROM_RTE(RTE, Address),
|
|
RTE->rte_if
|
|
);
|
|
|
|
if (SrcNTE == NULL) {
|
|
// Can't find an address! Fail the request.
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
|
|
Src = SrcNTE->nte_addr;
|
|
}
|
|
}
|
|
|
|
// Now, see if an RCE already exists for this.
|
|
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 = CTEAllocMem(sizeof(RouteCacheEntry));
|
|
*RCE = NewRCE;
|
|
|
|
if (NewRCE != NULL) {
|
|
CTEMemSet(NewRCE, 0, 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;
|
|
#ifdef _PNP_POWER
|
|
//* Update the ref. count for this interface.
|
|
NewRCE->rce_flags |= RCE_REFERENCED;
|
|
RTE->rte_if->if_refcount++;
|
|
#endif
|
|
}
|
|
NewRCE->rce_next = RTE->rte_rcelist;
|
|
RTE->rte_rcelist = NewRCE;
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return Src;
|
|
} 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;
|
|
CTEFreeLock(&OldRCE->rce_lock, RCEHandle);
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return Src;
|
|
}
|
|
} else {
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return NULL_IP_ADDR;
|
|
}
|
|
|
|
//* 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.
|
|
CTELockHandle RCEHandle;
|
|
Interface *IF;
|
|
|
|
if (RCE != NULL) {
|
|
CTEGetLock(&RouteTableLock, &TableLock);
|
|
CTEGetLock(&RCE->rce_lock, &RCEHandle);
|
|
|
|
if (--RCE->rce_cnt == 0) {
|
|
CTEAssert(RCE->rce_usecnt == 0);
|
|
if (RCE->rce_flags & RCE_VALID) {
|
|
// 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;
|
|
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;
|
|
}
|
|
|
|
CTEAssert(PrevRCE != NULL);
|
|
PrevRCE->rce_next = RCE->rce_next;
|
|
} else
|
|
IF = (Interface *)RCE->rce_rte;
|
|
|
|
if (RCE->rce_flags & RCE_CONNECTED) {
|
|
(*(IF->if_invalidate))(IF->if_lcontext, RCE);
|
|
}
|
|
|
|
#ifdef _PNP_POWER
|
|
if (RCE->rce_flags & RCE_REFERENCED) {
|
|
LockedDerefIF(IF);
|
|
}
|
|
#endif
|
|
CTEFreeLock(&RCE->rce_lock, RCEHandle);
|
|
CTEFreeMem(RCE);
|
|
}
|
|
else {
|
|
CTEFreeLock(&RCE->rce_lock, RCEHandle);
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, 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.
|
|
//
|
|
// 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)
|
|
{
|
|
uint RouteType; // SNMP route type.
|
|
RouteTableEntry *NewRTE, *OldRTE; // Entries for new and previous
|
|
// RTEs.
|
|
RouteTableEntry *PrevRTE; // Pointer to previous RTE.
|
|
CTELockHandle RCEHandle; // Lock handle for RCEs.
|
|
uint OldMetric; // Previous metric in use.
|
|
uint OldPriority; // Priority of previous route to
|
|
// destination.
|
|
RouteCacheEntry *CurrentRCE; // Current RCE being examined.
|
|
RouteCacheEntry *PrevRCE; // Previous RCE examined.
|
|
Interface *IF; // Interface being added on.
|
|
uint Priority; // Priority of the route.
|
|
uint TempMask; // Temporary copy of the mask.
|
|
uint Now = CTESystemUpTime() / 1000L; // System up time,
|
|
// in seconds.
|
|
uint MoveAny; // TRUE if we'll move any RCE.
|
|
ushort OldFlags;
|
|
|
|
// 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;
|
|
|
|
#ifdef _PNP_POWER
|
|
// If the interface is marked as going away, fail this.
|
|
if (OutIF->if_flags & IF_FLAGS_DELETING) {
|
|
return IP_BAD_REQ;
|
|
}
|
|
#endif
|
|
|
|
RouteType = IP_ADDR_EQUAL(FirstHop, IPADDR_LOCAL) ? IRE_TYPE_DIRECT :
|
|
IRE_TYPE_INDIRECT;
|
|
|
|
|
|
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) ) {
|
|
// This interface has NTEs attached, but none are valid. Fail the
|
|
// request.
|
|
return IP_BAD_REQ;
|
|
}
|
|
}
|
|
|
|
|
|
// First look and see if the RTE already exists.
|
|
NewRTE = FindSpecificRTE(Destination, Mask, FirstHop, OutIF, &PrevRTE);
|
|
|
|
if (NewRTE != NULL) {
|
|
|
|
// The RTE already exists, and we have a lock on it. See if we're
|
|
// updating the metric. If we're not, just return. Otherwise
|
|
// we'll remove the RTE and update the metric. If the metric is
|
|
// increasing, walk down the RCE chain on the RTE and invalidate
|
|
// the RCE back pointers so that they'll be revalidated upon
|
|
// transmits, and then reinsert the RTE. If the metric is
|
|
// decreasing we'll just fall through to the case below where we'll
|
|
// promote RCEs and insert the RTE.
|
|
|
|
|
|
// Update the things that won't cause routing table motion.
|
|
NewRTE->rte_mtu = MTU;
|
|
NewRTE->rte_proto = Proto;
|
|
NewRTE->rte_valid = Now;
|
|
NewRTE->rte_mtuchange = Now;
|
|
NewRTE->rte_context = Context;
|
|
OldFlags = NewRTE->rte_flags;
|
|
|
|
// We always turn off the increase flag when a route is updated, so
|
|
// that we take the long timeout to try to increase it. We also
|
|
// always turn on the valid flag.
|
|
NewRTE->rte_flags = (OldFlags & ~RTE_INCREASE) | RTE_VALID;
|
|
if (OutIF != (Interface *)&DummyInterface) {
|
|
NewRTE->rte_flags |= RTE_IF_VALID;
|
|
} else {
|
|
|
|
//
|
|
// Invalidating a previously valid route
|
|
//
|
|
|
|
NewRTE->rte_flags &= ~RTE_IF_VALID;
|
|
}
|
|
|
|
// 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. In the case bump the active default gateway count.
|
|
if (NewRTE->rte_mask == DEFAULT_MASK && !(OldFlags & RTE_VALID))
|
|
DefGWActive++;
|
|
|
|
// Need to update the metric, which will cause this RTE to move
|
|
// in the table.
|
|
OldMetric = NewRTE->rte_metric; // Save the old one.
|
|
RemoveRTE(PrevRTE, NewRTE); // Pull him from the chain.
|
|
NewRTE->rte_metric = Metric;
|
|
|
|
// Now see if we're increasing or decreasing the metric, or
|
|
// re-validating a previously invalid route. By definition, if the
|
|
// route wasn't valid before this there can't have been any RCEs
|
|
// attached to it, so we'll fall through to the promotion code
|
|
// and see if we can move any onto it. Otherwise, if we're
|
|
// demoting this route invalidate any RCEs on it.
|
|
|
|
if (Metric >= OldMetric && (OldFlags & RTE_VALID)) {
|
|
// We're increasing it, or leaving it the same. His valid state
|
|
// may have changed, so we'll we'll invalidate any RCEs on him
|
|
// now in any case.
|
|
InsertRTE(NewRTE);
|
|
|
|
InvalidateRCEChain(NewRTE);
|
|
|
|
//
|
|
// Whether the IF has changed or not, we can always overwrite it
|
|
// We wait till here to overwrite because if old rte_if was
|
|
// not dummy if, then we will want to invalidate RCEChain
|
|
// Since the invalidate function is stored in the interface
|
|
// structure we want to keep it around
|
|
//
|
|
|
|
NewRTE->rte_if = OutIF;
|
|
|
|
return IP_SUCCESS;
|
|
}
|
|
|
|
|
|
NewRTE->rte_if = OutIF;
|
|
|
|
// The metric is less than the old metric, so we're promoting this
|
|
// RTE. Fall through to the code below that deals with this, after
|
|
// saving the priority of the RTE.
|
|
Priority = NewRTE->rte_priority;
|
|
|
|
} else {
|
|
|
|
// Didn't find a matching RTE. Allocate a new one, and fill it in.
|
|
NewRTE = CTEAllocMem(sizeof(RouteTableEntry));
|
|
|
|
if (NewRTE == NULL) {
|
|
// Couldn't get the memory.
|
|
return IP_NO_RESOURCES;
|
|
}
|
|
|
|
IPSInfo.ipsi_numroutes++;
|
|
|
|
// Fill him in, and take the lock on him. To do this we'll need to
|
|
// calculate the priority.
|
|
Priority = 0;
|
|
TempMask = Mask;
|
|
|
|
while (TempMask) {
|
|
Priority += TempMask & 1;
|
|
TempMask >>= 1;
|
|
}
|
|
|
|
// Now initialize all of his fields.
|
|
NewRTE->rte_priority = Priority;
|
|
|
|
NewRTE->rte_dest = Destination;
|
|
NewRTE->rte_mask = Mask;
|
|
if (Mask == DEFAULT_MASK) {
|
|
// We're adding a default route.
|
|
DefGWConfigured++;
|
|
DefGWActive++;
|
|
}
|
|
NewRTE->rte_addr = FirstHop;
|
|
NewRTE->rte_metric = Metric;
|
|
NewRTE->rte_mtu = MTU;
|
|
NewRTE->rte_if = OutIF;
|
|
NewRTE->rte_rcelist = NULL;
|
|
NewRTE->rte_type = (ushort)RouteType;
|
|
NewRTE->rte_flags = RTE_VALID;
|
|
if (OutIF != (Interface *)&DummyInterface) {
|
|
NewRTE->rte_flags |= RTE_IF_VALID;
|
|
}
|
|
NewRTE->rte_proto = Proto;
|
|
NewRTE->rte_valid = Now;
|
|
NewRTE->rte_mtuchange = Now;
|
|
NewRTE->rte_admintype = AType;
|
|
NewRTE->rte_context = Context;
|
|
}
|
|
|
|
// At this point, NewRTE points to an initialized RTE that is not in the
|
|
// table. We hold the lock on NewRTE and on the RouteTable. First we'll
|
|
// find where we'll eventually insert the new RTE (we can't insert it
|
|
// yet, because we'll still need to search the table again and we can't
|
|
// do that while we hold a lock on an element in the table). Then we'll
|
|
// search the table for the next best route (i.e. a route with a priority
|
|
// less than or equal to ours), and if we find one we'll promote RCEs from
|
|
// that route to us. We'll actually have to search a chain of RTEs.
|
|
|
|
PrevRTE = FindInsertPoint(NewRTE);
|
|
OldRTE = LookupRTE(Destination, NULL_IP_ADDR, Priority);
|
|
|
|
// If we found one, try to promote from him.
|
|
if (OldRTE != NULL) {
|
|
|
|
// Found another RTE, and we have the lock on it. Starting with him,
|
|
// walk down the chain. At the start of this loop we know that the
|
|
// route described by OldRTE can reach our destination. If his metric
|
|
// is better than ours, we're done and we'll just insert our route.
|
|
// If his priority and metric are equal to ours we'll promote only
|
|
// those RCEs that exactly match our source address. Otherwise either
|
|
// his priority or metric is worse than ours, and we'll promote all
|
|
// appropriate RCEs.
|
|
//
|
|
// Since we specified the maximum priority in the call to LookupRTE
|
|
// as our priority, we know the priority of the route we found
|
|
// can't be greater than our priority.
|
|
|
|
OldMetric = OldRTE->rte_metric;
|
|
OldPriority = OldRTE->rte_priority;
|
|
|
|
// We'll do the search if his priority is less than ours, or his
|
|
// metric is > (i.e. worse than) our metric, or his metric equals
|
|
// our metric and the old route is on a different interface. We
|
|
// know OldPriority is <= our Priority, since we specified our
|
|
// priority in the call to LookupRTE above.
|
|
|
|
CTEAssert(OldPriority <= Priority);
|
|
|
|
if (OldPriority < Priority || OldMetric > Metric ||
|
|
(OldMetric == Metric && OldRTE->rte_if != OutIF)) {
|
|
|
|
// We're going to search. Figure out the mask to use in comparing
|
|
// source addresses.
|
|
if (OldPriority == Priority && OldMetric == Metric)
|
|
MoveAny = FALSE; // Only promote an exact match.
|
|
else
|
|
MoveAny = TRUE; // Promote any match.
|
|
|
|
for (;;) {
|
|
IF = OldRTE->rte_if;
|
|
|
|
PrevRCE = STRUCT_OF(RouteCacheEntry, &OldRTE->rte_rcelist,
|
|
rce_next);
|
|
CurrentRCE = PrevRCE->rce_next;
|
|
// Walk the list, promoting any that match.
|
|
while (CurrentRCE != NULL) {
|
|
CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle);
|
|
|
|
// If the masked source address matches our masked address,
|
|
// and the destinations match, promote him.
|
|
if ((MoveAny || AddrOnIF(OutIF, CurrentRCE->rce_src)) &&
|
|
IP_ADDR_EQUAL(CurrentRCE->rce_dest & Mask, Destination)) {
|
|
// He matches. Pull him from the list and mark him as invalid.
|
|
// This will force a new lookup of the route next time he's
|
|
// used, which as a side effect will cause the route to be
|
|
// connected in the dial-on-demand case.
|
|
CTEAssert(CurrentRCE->rce_flags & RCE_VALID);
|
|
PrevRCE->rce_next = CurrentRCE->rce_next;
|
|
CurrentRCE->rce_flags &= ~RCE_VALID;
|
|
CurrentRCE->rce_rte = (RouteTableEntry *)IF;
|
|
if (CurrentRCE->rce_usecnt == 0) {
|
|
// No one's currently using him, so invalidate him.
|
|
if (CurrentRCE->rce_flags & RCE_CONNECTED) {
|
|
(*(IF->if_invalidate))(IF->if_lcontext, CurrentRCE);
|
|
#ifdef _PNP_POWER
|
|
if (CurrentRCE->rce_flags & RCE_REFERENCED) {
|
|
LockedDerefIF(IF);
|
|
CurrentRCE->rce_flags &= ~RCE_REFERENCED;
|
|
}
|
|
#endif
|
|
} else {
|
|
#ifdef _PNP_POWER
|
|
CTEAssert(!(CurrentRCE->rce_flags & RCE_REFERENCED));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// Doesn't match. Try the next one.
|
|
PrevRCE = CurrentRCE;
|
|
}
|
|
CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle);
|
|
CurrentRCE = PrevRCE->rce_next;
|
|
}
|
|
|
|
// We've walked the RCE list on that old RTE. Look at the
|
|
// next one. If it has the same priority and metric as the
|
|
// old one, and also matches our destination, check it also. We
|
|
// don't need to RTEs that don't match this criteria, since we know
|
|
// RCEs are always kept on the 'best' RTE.
|
|
OldRTE = OldRTE->rte_next;
|
|
if (OldRTE != NULL) {
|
|
// We have another one. Check it out.
|
|
if (OldRTE->rte_priority == Priority &&
|
|
OldRTE->rte_metric == OldMetric &&
|
|
IP_ADDR_EQUAL(Destination & OldRTE->rte_mask,
|
|
OldRTE->rte_dest))
|
|
continue; // It matches, so try to promote
|
|
// RCEs.
|
|
}
|
|
break; // Exit out of the for (;;) loop.
|
|
}
|
|
} else {
|
|
// OldRTE is a better route than the one we're inserting, so don't
|
|
// do anything.
|
|
}
|
|
}
|
|
|
|
// At this point we're promoted any routes we need to, we hold the lock
|
|
// on NewRTE which still isn't inserted, and PrevRTE describes where to
|
|
// insert NewRTE. Insert it, free the lock, and return success.
|
|
InsertAfterRTE(PrevRTE, NewRTE);
|
|
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)
|
|
{
|
|
CTELockHandle TableHandle;
|
|
IP_STATUS Status;
|
|
|
|
CTEGetLock(&RouteTableLock, &TableHandle);
|
|
Status = LockedAddRoute(Destination, Mask, FirstHop, OutIF, MTU, Metric,
|
|
Proto, AType, Context);
|
|
|
|
CTEFreeLock(&RouteTableLock, TableHandle);
|
|
return Status;
|
|
}
|
|
|
|
//* 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.
|
|
//
|
|
// Returns: Status of attempt to delete route.
|
|
//
|
|
IP_STATUS
|
|
DeleteRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop, Interface *OutIF)
|
|
{
|
|
RouteTableEntry *RTE; // RTE being deleted.
|
|
RouteTableEntry *PrevRTE; // Pointer to RTE in front of one
|
|
// being deleted.
|
|
CTELockHandle TableLock; // Lock handle for table.
|
|
|
|
|
|
// Look up the route by calling FindSpecificRTE. If we can't find it,
|
|
// fail the call.
|
|
CTEGetLock(&RouteTableLock, &TableLock);
|
|
RTE = FindSpecificRTE(Destination, Mask, FirstHop, OutIF, &PrevRTE);
|
|
|
|
if (RTE == NULL) {
|
|
// Didn't find the route, so fail the call.
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return IP_BAD_ROUTE;
|
|
}
|
|
|
|
#ifndef NT
|
|
//
|
|
// Disable admin check for NT, because the RAS server needs to be able to
|
|
// delete a subnet route. This ability is restricted to Administrators only
|
|
// by NT security checks.
|
|
//
|
|
//
|
|
if (RTE->rte_admintype == ATYPE_PERM) {
|
|
// Can't delete a permanent route.
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return IP_BAD_REQ;
|
|
}
|
|
#endif
|
|
|
|
// When we reach here we hold the lock on the RTE to be deleted, and
|
|
// PrevRTE points to the RTE immediately ahead of ours in the table.
|
|
// Call DeleteRTE to delete him.
|
|
DeleteRTE(PrevRTE, RTE);
|
|
|
|
CTEFreeLock(&RouteTableLock, TableLock);
|
|
return IP_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
//** 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;
|
|
|
|
if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR) || IP_LOOPBACK(FirstHop))
|
|
return; // Can't redirect to loopback
|
|
// address.
|
|
|
|
CTEAssert(IP_ADDR_EQUAL(NTE->nte_addr, Src));
|
|
|
|
// 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, &Handle);
|
|
|
|
// Make sure the source of the redirect is the current first hop gateway.
|
|
RTE = LookupRTE(Target, Src, HOST_ROUTE_PRI);
|
|
if (RTE == NULL || IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL) ||
|
|
!IP_ADDR_EQUAL(RTE->rte_addr, RDSrc)) {
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
return; // A bad redirect.
|
|
}
|
|
|
|
CTEAssert(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, 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);
|
|
if (RTE == NULL) {
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
return; // Can't get there from here.
|
|
}
|
|
|
|
CTEAssert(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, Handle);
|
|
return;
|
|
}
|
|
|
|
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.
|
|
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);
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
}
|
|
|
|
//* 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;
|
|
|
|
// If we're not doing PMTU discovery, don't do anything.
|
|
if (PMTUDiscovery) {
|
|
|
|
// We're doing PMTU discovery. 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, &Handle);
|
|
|
|
// Find an RTE for the destination.
|
|
RTE = LookupRTE(IPH->iph_dest, IPH->iph_src, HOST_ROUTE_PRI);
|
|
|
|
// If we couldn't find one, or the existing MTU is less than the new
|
|
// MTU, give up now.
|
|
|
|
if (RTE == NULL || (OldMTU = RTE->rte_mtu) < NewMTU) {
|
|
// No RTE, or an invalid new MTU. Just bail out now.
|
|
CTEFreeLock(&RouteTableLock, 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 -= 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 = OldMTU;
|
|
else
|
|
// Move down the table to the next lowest MTU.
|
|
NewMTU = GuessNewMTU(DGLength);
|
|
}
|
|
|
|
// We have the new MTU. Now add it to the table as a host route.
|
|
if (NewMTU != OldMTU)
|
|
LockedAddRoute(IPH->iph_dest, HOST_MASK, RTE->rte_addr, RTE->rte_if,
|
|
NewMTU, RTE->rte_metric, IRE_PROTO_ICMP, ATYPE_OVERRIDE,
|
|
RTE->rte_context);
|
|
|
|
CTEFreeLock(&RouteTableLock, 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);
|
|
|
|
}
|
|
}
|
|
|
|
//** 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;
|
|
uint i;
|
|
RouteTableEntry *RTE, *PrevRTE;
|
|
uint RaiseMTU, Delta;
|
|
Interface *IF;
|
|
IPAddr Dest;
|
|
uint NewMTU;
|
|
NetTableEntry *NTE;
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
for (i = 0; i < ROUTE_TABLE_SIZE; i++) {
|
|
// Walk down each chain, looking at the host routes. If we're
|
|
// doing PMTU discovery, see if we can raise the MTU.
|
|
PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], rte_next);
|
|
RTE = RouteTable[i];
|
|
while (RTE != NULL && RTE->rte_mask == HOST_MASK) {
|
|
// Make sure he's valid.
|
|
if (RTE->rte_flags & RTE_VALID) {
|
|
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) {
|
|
|
|
RTE->rte_flags |= RTE_INCREASE;
|
|
// This is a candidate for change. Figure out
|
|
// what it should be.
|
|
NewMTU = MIN(GetRaisedMTU(RTE->rte_mtu),
|
|
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, 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 start from the first element of this
|
|
// chain in case something's changed after we
|
|
// free the lock. We've updated the mtuchange
|
|
// time of this RTE, so we won't hit him again.
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i],
|
|
rte_next);
|
|
RTE = RouteTable[i];
|
|
continue;
|
|
} 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 DeleteRTE to do this.
|
|
DeleteRTE(PrevRTE, RTE);
|
|
RTE = PrevRTE->rte_next;
|
|
continue;
|
|
}
|
|
}
|
|
PrevRTE = RTE;
|
|
RTE = RTE->rte_next;
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL);
|
|
|
|
}
|
|
|
|
//* FreeFWPacket - Free a forwarding packet when we're done with it.
|
|
//
|
|
//
|
|
// Input: Packet - Packet to be freed.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
FreeFWPacket(PNDIS_PACKET Packet)
|
|
{
|
|
CTELockHandle Handle;
|
|
FWContext *FWC;
|
|
|
|
// BUGBUG - Portability issue
|
|
|
|
#ifdef VXD
|
|
Packet->Private.Head = (PNDIS_BUFFER)NULL;
|
|
Packet->Private.Count = 0;
|
|
Packet->Private.PhysicalCount = 0;
|
|
Packet->Private.TotalLength = 0;
|
|
#else // VXD
|
|
#ifdef NT
|
|
//
|
|
// BUGBUG: This is inefficient. Need something better.
|
|
//
|
|
NdisReinitializePacket(Packet);
|
|
#else // NT
|
|
#error Need portable way to do this.
|
|
#endif // NT
|
|
#endif // VXD
|
|
|
|
FWC = (FWContext *)Packet->ProtocolReserved;
|
|
if (FWC->fc_options) {
|
|
CTEFreeMem(FWC->fc_options);
|
|
FWC->fc_options = (uchar *)NULL;
|
|
}
|
|
|
|
if (FWC->fc_buffhead) {
|
|
CTEGetLock(&FWBufFreeLock, &Handle);
|
|
FWC->fc_bufftail->Next = FWBufFree; // BUGBUG more portable.
|
|
FWBufFree = FWC->fc_buffhead;
|
|
CTEFreeLock(&FWBufFreeLock, Handle);
|
|
FWC->fc_buffhead = (PNDIS_BUFFER)NULL;
|
|
}
|
|
#ifdef _PNP_POWER
|
|
// If there's an interface pointer here, dereference in now.
|
|
if (FWC->fc_if != NULL) {
|
|
DerefIF(FWC->fc_if);
|
|
FWC->fc_if = NULL;
|
|
}
|
|
#endif
|
|
|
|
CTEGetLock(&FWPacketFreeLock, &Handle);
|
|
FWC->fc_pc.pc_common.pc_link = FWPacketFree;
|
|
FWPacketFree = Packet;
|
|
CTEFreeLock(&FWPacketFreeLock, Handle);
|
|
|
|
}
|
|
|
|
//* GrowFWPackets - Grow the FW packet list, if we can.
|
|
//
|
|
// Called when we need to allocate a FW packet, but don't have one. We'll try to grow the
|
|
// FWPacket list now.
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: TRUE if we succeeded in growing the list, FALSE otherwise.
|
|
//
|
|
uint
|
|
GrowFWPackets(void)
|
|
{
|
|
CTELockHandle Handle;
|
|
IPHeader *HeaderPtr;
|
|
NDIS_HANDLE BufferPool;
|
|
NDIS_HANDLE PacketPool;
|
|
PNDIS_BUFFER Buffer;
|
|
PNDIS_PACKET Packet;
|
|
NDIS_STATUS Status;
|
|
uint i;
|
|
uint AmountToGrow;
|
|
|
|
CTEGetLock(&FWPacketFreeLock, &Handle);
|
|
|
|
AmountToGrow = MIN(MaxFWPackets - CurrentFWPackets, FWPACKET_GROW_AMOUNT);
|
|
HeaderPtr = NULL;
|
|
|
|
if (AmountToGrow != 0) {
|
|
|
|
// We have room to grow yet, so try to. First get the memory for our header buffers.
|
|
HeaderPtr = CTEAllocMem(AmountToGrow * sizeof(IPHeader));
|
|
if (HeaderPtr == (IPHeader *)NULL)
|
|
goto failure; // Couldn't get it.
|
|
|
|
// Now try to get NDIS buffers for the headers.
|
|
NdisAllocateBufferPool(&Status, &BufferPool, AmountToGrow);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
goto failure;
|
|
}
|
|
|
|
// Now try to get the packets themselves.
|
|
NdisAllocatePacketPool(&Status, &PacketPool, AmountToGrow, sizeof(FWContext));
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
NdisFreeBufferPool(BufferPool);
|
|
goto failure;
|
|
}
|
|
|
|
// Since we have everything we need, update the current count.
|
|
CurrentFWPackets += AmountToGrow;
|
|
|
|
CTEFreeLock(&FWPacketFreeLock, Handle);
|
|
|
|
// We got the resources we need. Loop through and put them on.
|
|
for (i = 0; i < AmountToGrow; i++) {
|
|
FWContext *FWC;
|
|
|
|
NdisAllocateBuffer(&Status, &Buffer, BufferPool, HeaderPtr,
|
|
sizeof(IPHeader));
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
CTEAssert(FALSE);
|
|
NdisAllocatePacket(&Status, &Packet, PacketPool);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
CTEAssert(FALSE);
|
|
|
|
CTEMemSet(Packet->ProtocolReserved, 0, sizeof(FWContext));
|
|
FWC = (FWContext *)Packet->ProtocolReserved;
|
|
FWC->fc_hndisbuff = Buffer;
|
|
FWC->fc_hbuff = HeaderPtr;
|
|
FWC->fc_pc.pc_common.pc_flags = PACKET_FLAG_FW;
|
|
FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP;
|
|
FWC->fc_pc.pc_pi = RtPI;
|
|
FWC->fc_pc.pc_context = Packet;
|
|
|
|
FreeFWPacket(Packet);
|
|
HeaderPtr++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
failure:
|
|
CTEFreeLock(&FWPacketFreeLock, Handle);
|
|
if (HeaderPtr != NULL) {
|
|
CTEFreeMem(HeaderPtr);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//* GrowFWBuffer - Grow the FW buffer pool, if we can.
|
|
//
|
|
// Called when we need to grow the FW buffer pool. We'll grow it up to the maximum size
|
|
// specified by the user.
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: TRUE if we succeeded in growing the pool, FALSE otherwise.
|
|
//
|
|
uint
|
|
GrowFWBuffer(void)
|
|
{
|
|
CTELockHandle Handle;
|
|
uint AvailableBufferSpace;
|
|
uint NewBufferCount;
|
|
uint i;
|
|
uchar *BufferPtr = NULL;
|
|
NDIS_STATUS Status;
|
|
PNDIS_BUFFER Buffer;
|
|
NDIS_HANDLE BufferPool;
|
|
|
|
CTEGetLock(&FWPacketFreeLock, &Handle);
|
|
AvailableBufferSpace = MIN(MaxFWBufferSize - CurrentFWBufferSize, FW_BUF_GROW_AMOUNT);
|
|
|
|
// If we have room to grow, do so.
|
|
if (AvailableBufferSpace >= FW_BUF_SIZE) {
|
|
// We have room to grow the buffer, so do so. First, round to a multiple of our
|
|
// FW buffer size.
|
|
NewBufferCount = AvailableBufferSpace / FW_BUF_SIZE;
|
|
AvailableBufferSpace = NewBufferCount * FW_BUF_SIZE;
|
|
|
|
// Allocate the resources we need.
|
|
BufferPtr = CTEAllocMem(AvailableBufferSpace);
|
|
if (BufferPtr == NULL) {
|
|
goto failure;
|
|
}
|
|
|
|
NdisAllocateBufferPool(&Status, &BufferPool, NewBufferCount);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
goto failure;
|
|
}
|
|
|
|
// We got what we needed. Now loop through and put them on the list.
|
|
for (i = 0; i < NewBufferCount; i++) {
|
|
NdisAllocateBuffer(&Status, &Buffer, BufferPool, BufferPtr, FW_BUF_SIZE);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
CTEAssert(FALSE);
|
|
|
|
Buffer->Next = FWBufFree;
|
|
FWBufFree = Buffer;
|
|
BufferPtr += FW_BUF_SIZE;
|
|
}
|
|
|
|
CurrentFWBufferSize += AvailableBufferSpace;
|
|
CTEFreeLock(&FWPacketFreeLock, Handle);
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
failure:
|
|
CTEFreeLock(&FWPacketFreeLock, Handle);
|
|
if (BufferPtr != NULL) {
|
|
CTEFreeMem(BufferPtr);
|
|
}
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//* 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)
|
|
{
|
|
PNDIS_PACKET Packet = (PNDIS_PACKET)SendContext;
|
|
FWContext *FWC = (FWContext *)Packet->ProtocolReserved;
|
|
RouteSendQ *RSQ;
|
|
CTELockHandle Handle;
|
|
FWQ *NewFWQ;
|
|
PNDIS_PACKET NewPacket;
|
|
|
|
|
|
#ifdef DEBUG
|
|
if (!Buffer)
|
|
DEBUGCHK;
|
|
#endif
|
|
|
|
if (!IS_BCAST_DEST(FWC->fc_dtype))
|
|
RSQ = &((RouteInterface *)FWC->fc_if)->ri_q;
|
|
else
|
|
RSQ = BCastRSQ;
|
|
|
|
FreeFWPacket(Packet);
|
|
|
|
CTEGetLock(&RSQ->rsq_lock, &Handle);
|
|
CTEAssert(RSQ->rsq_pending <= RSQ->rsq_maxpending);
|
|
|
|
RSQ->rsq_pending--;
|
|
|
|
CTEAssert(*(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) {
|
|
#ifdef DEBUG
|
|
if (RSQ->rsq_qh.fq_next == &RSQ->rsq_qh)
|
|
DEBUGCHK; // Empty Q!
|
|
#endif
|
|
// 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;
|
|
PVOID VirtualAddress;
|
|
UINT BufLen;
|
|
|
|
// Fix up the packet. Remove the existing buffer chain, and put our header on
|
|
// the front.
|
|
|
|
// BUGBUG - Get NDIS fixed to make this portable.
|
|
#ifdef VXD
|
|
Buffer = Packet->Private.Head;
|
|
HBuffer = FC->fc_hndisbuff;
|
|
Packet->Private.Head = HBuffer;
|
|
Packet->Private.Tail = HBuffer;
|
|
HBuffer->Next = (PNDIS_BUFFER)NULL;
|
|
Packet->Private.TotalLength = sizeof(IPHeader);
|
|
Packet->Private.Count = 1;
|
|
|
|
Packet->Private.PhysicalCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(HBuffer->VirtualAddress,
|
|
sizeof(IPHeader));
|
|
#else // VXD
|
|
#ifdef NT
|
|
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;
|
|
|
|
NdisQueryBuffer(HBuffer, &VirtualAddress, &BufLen);
|
|
|
|
Packet->Private.PhysicalCount =
|
|
ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
|
VirtualAddress,
|
|
sizeof(IPHeader)
|
|
);
|
|
#else // NT
|
|
#error HELP! Need to make this code portable.
|
|
#endif // NT
|
|
#endif // VXD
|
|
|
|
// 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)
|
|
Status = SendIPPacket(FC->fc_if, FC->fc_nexthop, Packet, Buffer,
|
|
FC->fc_hbuff, FC->fc_options, (uint)FC->fc_optlength);
|
|
else { // Need to fragment this.
|
|
BufferReference *BR = CTEAllocMem(sizeof(BufferReference));
|
|
|
|
if (BR == (BufferReference *)NULL) { // Couldn't get a BufferReference
|
|
FWSendComplete(Packet, Buffer);
|
|
return;
|
|
}
|
|
BR->br_buffer = Buffer;
|
|
BR->br_refcount = 0;
|
|
CTEInitLock(&BR->br_lock);
|
|
FC->fc_pc.pc_br = BR;
|
|
Status = IPFragment(FC->fc_if, FC->fc_mtu, FC->fc_nexthop, Packet,
|
|
FC->fc_hbuff, Buffer, DataLength, FC->fc_options,
|
|
(uint)FC->fc_optlength, (int *)NULL);
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
CTEAssert(Status != IP_PACKET_TOO_BIG);
|
|
}
|
|
} else
|
|
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)
|
|
FWSendComplete(Packet, Buffer);
|
|
}
|
|
|
|
//* 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++;
|
|
FreeFWPacket(Packet);
|
|
}
|
|
|
|
}
|
|
|
|
//* RemoveRandomFWPacket - Remove a random packet from the FW queue.
|
|
//
|
|
// Called when we run out of resources. We pick a random packet from the FW queue,
|
|
// free it, and return. The caller will hopefully then get it for his own use.
|
|
//
|
|
// Input: RSQ - Pointer to outgoing route send q..
|
|
//
|
|
// Returns: TRUE if we free a packet, false if we didn't.
|
|
//
|
|
uchar
|
|
RemoveRandomFWPacket(RouteSendQ *RSQ)
|
|
{
|
|
uint Now = (uint)CTESystemUpTime();
|
|
CTELockHandle Handle;
|
|
uint PacketCount;
|
|
PNDIS_PACKET FreedPacket;
|
|
FWQ *CurrentFWQ;
|
|
#ifdef DEBUG
|
|
FWQ *FirstFWQ;
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
FirstFWQ = &RSQ->rsq_qh;
|
|
#endif
|
|
|
|
CTEGetLock(&RSQ->rsq_lock, &Handle);
|
|
if (RSQ->rsq_qlength) { // We have a least one in the list.
|
|
|
|
|
|
PacketCount = Now % (RSQ->rsq_qlength + 1);
|
|
if (PacketCount == RSQ->rsq_qlength) {
|
|
CTEFreeLock(&RSQ->rsq_lock, Handle);
|
|
return FALSE;
|
|
}
|
|
|
|
CurrentFWQ = RSQ->rsq_qh.fq_next;
|
|
while (PacketCount--) {
|
|
#ifdef DEBUG
|
|
if (CurrentFWQ == FirstFWQ)
|
|
DEBUGCHK;
|
|
#endif
|
|
CurrentFWQ = CurrentFWQ->fq_next;
|
|
}
|
|
|
|
// We've got the proper packet. Splice him out.
|
|
CurrentFWQ->fq_next->fq_prev = CurrentFWQ->fq_prev;
|
|
CurrentFWQ->fq_prev->fq_next = CurrentFWQ->fq_next;
|
|
RSQ->rsq_qlength--;
|
|
CTEFreeLock(&RSQ->rsq_lock, Handle);
|
|
FreedPacket = PACKET_FROM_FWQ(CurrentFWQ);
|
|
FreeFWPacket(FreedPacket);
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return TRUE;
|
|
}
|
|
CTEFreeLock(&RSQ->rsq_lock, Handle);
|
|
return FALSE;
|
|
|
|
|
|
}
|
|
|
|
//* 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)
|
|
{
|
|
CTELockHandle Handle;
|
|
uint BufNeeded, i;
|
|
PNDIS_BUFFER FirstBuffer, CurrentBuffer;
|
|
void *DestPtr;
|
|
Interface *SrcIF;
|
|
FWContext *FWC;
|
|
uint BufLen;
|
|
uint LastBufSize;
|
|
#ifdef DEBUG
|
|
uint TotalBufferSize;
|
|
PNDIS_BUFFER TempBuffer;
|
|
#endif
|
|
|
|
// Figure out how many buffers we need.
|
|
BufNeeded = DataLength / FW_BUF_SIZE;
|
|
LastBufSize = DataLength % FW_BUF_SIZE;
|
|
if (LastBufSize != 0)
|
|
BufNeeded++;
|
|
|
|
#ifdef DEBUG
|
|
if (!BufNeeded)
|
|
DEBUGCHK;
|
|
#endif
|
|
FWC = (FWContext *)Packet->ProtocolReserved;
|
|
|
|
// Now run down the buffer free list, getting the buffers we need. If we
|
|
// can't get enough the first time, we'll free a random packet from our
|
|
// pending list and try again.
|
|
for (;;) {
|
|
CTEGetLock(&FWBufFreeLock, &Handle);
|
|
FirstBuffer = FWBufFree;
|
|
CurrentBuffer = STRUCT_OF(NDIS_BUFFER, &FWBufFree, Next);
|
|
i = 0;
|
|
do {
|
|
CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer);
|
|
if (!CurrentBuffer)
|
|
break;
|
|
|
|
// Zap this buffer length to the full buffer size, since it may
|
|
// have been modified from a previous send.
|
|
NdisAdjustBufferLength(CurrentBuffer, FW_BUF_SIZE);
|
|
i++;
|
|
} while (i < BufNeeded);
|
|
|
|
if (i != BufNeeded) { // We ran out of buffers. Free a packet and try again.
|
|
RouteSendQ *RSQ;
|
|
|
|
if ((MaxFWBufferSize - CurrentFWBufferSize) <= FW_BUF_SIZE) {
|
|
CTEFreeLock(&FWBufFreeLock, Handle);
|
|
if (GrowFWBuffer()) {
|
|
continue;
|
|
}
|
|
} else
|
|
CTEFreeLock(&FWBufFreeLock, Handle);
|
|
|
|
if (!IS_BCAST_DEST(FWC->fc_dtype))
|
|
RSQ = &((RouteInterface *)FWC->fc_if)->ri_q;
|
|
else
|
|
RSQ = BCastRSQ;
|
|
|
|
if (!RemoveRandomFWPacket(RSQ)) {
|
|
if (IS_BCAST_DEST(FWC->fc_dtype))
|
|
return NDIS_STATUS_RESOURCES;
|
|
|
|
// Couldn't get one for a non-broadcast packet. If the qlen is
|
|
// 0 on the outgoing queue, we'll try other queues, on the
|
|
// presumption that traffic through some other interface is
|
|
// starving this one.
|
|
if (RSQ->rsq_qlength == 0) {
|
|
Interface *IF;
|
|
for (IF = IFList; IF != NULL; IF = IF->if_next) {
|
|
RSQ = &((RouteInterface *)IF)->ri_q;
|
|
if (RemoveRandomFWPacket(RSQ))
|
|
break;
|
|
}
|
|
if (IF == NULL)
|
|
return NDIS_STATUS_RESOURCES;
|
|
} else
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
// Otherwise we'll fall through and try again, now that we have
|
|
// hopefully put some more on the free queue.
|
|
|
|
} else {
|
|
// We have as many as we need. Update the free list.
|
|
FWBufFree = NDIS_BUFFER_LINKAGE(CurrentBuffer);
|
|
CTEFreeLock(&FWBufFreeLock, Handle);
|
|
NDIS_BUFFER_LINKAGE(CurrentBuffer) = (PNDIS_BUFFER)NULL;
|
|
|
|
// If we have a non-full last buffer, adjust it's size.
|
|
if (LastBufSize != 0)
|
|
NdisAdjustBufferLength(CurrentBuffer, LastBufSize);
|
|
|
|
FWC->fc_buffhead = FirstBuffer;
|
|
FWC->fc_bufftail = CurrentBuffer;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NdisChainBufferAtFront(Packet, FirstBuffer);
|
|
|
|
#ifdef DEBUG
|
|
// Sanity check the buffer chain and packet.
|
|
TempBuffer = FirstBuffer;
|
|
TotalBufferSize = 0;
|
|
while (TempBuffer != NULL) {
|
|
TotalBufferSize += NdisBufferLength(TempBuffer);
|
|
TempBuffer = NDIS_BUFFER_LINKAGE(TempBuffer);
|
|
}
|
|
|
|
CTEAssert(TotalBufferSize == DataLength);
|
|
NdisQueryPacket(Packet, NULL, NULL, NULL, &TotalBufferSize);
|
|
CTEAssert(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)) {
|
|
while (DataLength) {
|
|
uint CopyLength;
|
|
|
|
#ifdef VXD
|
|
DestPtr = FirstBuffer->VirtualAddress;
|
|
#else
|
|
//
|
|
// BUGBUG: This is inefficient.
|
|
//
|
|
NdisQueryBuffer(FirstBuffer, &DestPtr, &BufLen);
|
|
#endif
|
|
CopyLength = MIN(DataLength, FW_BUF_SIZE);
|
|
CTEAssert(CopyLength == NdisBufferLength(FirstBuffer));
|
|
CTEMemCopy(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.
|
|
//
|
|
// Entry: Packet - Pointer to where to return a packet.
|
|
// IF - Outgoing I/F for packet.
|
|
// DestType - Type of outgoing packet.
|
|
//
|
|
// Returns: Pointer to header buffer.
|
|
//
|
|
//
|
|
IPHeader *
|
|
GetFWPacket(PNDIS_PACKET *Packet, Interface *IF, uchar DestType)
|
|
{
|
|
CTELockHandle Handle;
|
|
PNDIS_PACKET NewPacket;
|
|
RouteSendQ *RSQ;
|
|
|
|
for (;;) {
|
|
CTEGetLock(&FWPacketFreeLock, &Handle);
|
|
if ((NewPacket = FWPacketFree) != (PNDIS_PACKET)NULL) {
|
|
FWContext *FWC;
|
|
|
|
FWC = (FWContext *)NewPacket->ProtocolReserved;
|
|
FWPacketFree = FWC->fc_pc.pc_common.pc_link;
|
|
FWC->fc_pc.pc_common.pc_flags |= PACKET_FLAG_IPHDR;
|
|
FWC->fc_pc.pc_br = NULL;
|
|
*Packet = NewPacket;
|
|
CTEFreeLock(&FWPacketFreeLock, Handle);
|
|
return FWC->fc_hbuff;
|
|
}
|
|
|
|
// If we couldn't get one, try to grow the list if we can.
|
|
if (MaxFWPackets > CurrentFWPackets) {
|
|
CTEFreeLock(&FWPacketFreeLock, Handle);
|
|
// We're allowed to grow, so try to.
|
|
if (GrowFWPackets()) {
|
|
continue; // If we grew it, try again.
|
|
}
|
|
} else
|
|
CTEFreeLock(&FWPacketFreeLock, Handle);
|
|
|
|
// Either we weren't allowed to grow the list, or we tried to but couldn't. Try yo
|
|
// get one that's on the queue already.
|
|
if (!IS_BCAST_DEST(DestType))
|
|
RSQ = &((RouteInterface *)IF)->ri_q;
|
|
else
|
|
RSQ = BCastRSQ;
|
|
|
|
if (!RemoveRandomFWPacket(RSQ))
|
|
break;
|
|
}
|
|
|
|
return (IPHeader *)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.
|
|
// DestType - Type of destination.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
IPForward(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header, uint HeaderLength,
|
|
void *Data, uint BufferLength, NDIS_HANDLE LContext1, uint LContext2,
|
|
uchar DestType)
|
|
{
|
|
uchar *Options;
|
|
uchar OptLength;
|
|
OptIndex Index;
|
|
IPAddr DestAddr; // IP address we're routing towards.
|
|
uchar SendOnSource = FALSE;
|
|
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;
|
|
|
|
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 (!ForwardBCast) {
|
|
if (DestType > DEST_REMOTE)
|
|
IPSInfo.ipsi_inaddrerrors++;
|
|
return;
|
|
}
|
|
if ((DestAddr == IP_LOCAL_BCST) ||
|
|
(DestAddr == IP_ZERO_BCST) ||
|
|
(DestType == DEST_SN_BCAST) ||
|
|
CLASSD_ADDR(DestAddr)) {
|
|
return;
|
|
}
|
|
} else
|
|
if (DestType == DEST_REMOTE) {
|
|
SrcNTE = BestNTEForIF(Header->iph_src, SrcNTE->nte_if);
|
|
if (SrcNTE == NULL) {
|
|
// Something bad happened.
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If the TTL would expire, send a message.
|
|
if (Header->iph_ttl <= 1) {
|
|
IPSInfo.ipsi_inhdrerrors++;
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_TIME_EXCEED, TTL_IN_TRANSIT,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;
|
|
|
|
OptInfo.ioi_options = (uchar *)(Header + 1);
|
|
OptInfo.ioi_optlength = HeaderLength - sizeof(IPHeader);
|
|
// Validate options, and set up indices.
|
|
if ((ErrIndex = ParseRcvdOptions(&OptInfo, &Index)) < MAX_OPT_SIZE) {
|
|
IPSInfo.ipsi_inhdrerrors++;
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM,
|
|
PTR_VALID, ((ulong)ErrIndex + sizeof(IPHeader)));
|
|
return;
|
|
}
|
|
|
|
Options = CTEAllocMem(OptInfo.ioi_optlength);
|
|
if (!Options) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
return; // Couldn't get an
|
|
} // option buffer, return;
|
|
|
|
// Now copy into our buffer.
|
|
CTEMemCopy(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++;
|
|
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 (DestType == DEST_INVALID) {
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, SR_FAILED, 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 = TRUE;
|
|
}
|
|
}
|
|
} 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 = LookupNextHopWithBuffer(DestAddr, SrcNTE->nte_addr, &NextHop, &MTU,
|
|
Header->iph_protocol, (uchar *)Data, BufferLength);
|
|
|
|
if (IF == NULL) {
|
|
// Couldn't find an outgoing route.
|
|
IPSInfo.ipsi_outnoroutes++;
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
|
|
HOST_UNREACH, 0);
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
)
|
|
{
|
|
CTEAssert((MTU + sizeof(IPHeader)) >= 68);
|
|
CTEAssert((MTU + sizeof(IPHeader)) <= 0xFFFF);
|
|
|
|
IPSInfo.ipsi_fragfails++;
|
|
SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH,
|
|
FRAG_NEEDED, net_long((ulong)(MTU + sizeof(IPHeader)))
|
|
);
|
|
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
#ifdef _PNP_POWER
|
|
DerefIF(IF);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
// 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 (ForwardFilterPtr != NULL) {
|
|
FORWARD_ACTION Action;
|
|
|
|
Action = (*ForwardFilterPtr)(Header, Data, BufferLength,
|
|
SrcNTE->nte_if->if_filtercontext, IF->if_filtercontext);
|
|
|
|
if (Action != FORWARD) {
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
#ifdef _PNP_POWER
|
|
DerefIF(IF);
|
|
#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);
|
|
CTEFreeMem(Options);
|
|
#ifdef _PNP_POWER
|
|
DerefIF(IF);
|
|
#endif
|
|
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, &TableHandle);
|
|
OutNTE = BestNTEForIF(DestAddr, IF);
|
|
if (OutNTE == NULL) {
|
|
// No NTE for this IF. Something's wrong, just bail out.
|
|
CTEFreeLock(&RouteTableLock, TableHandle);
|
|
CTEFreeMem(Options);
|
|
#ifdef _PNP_POWER
|
|
DerefIF(IF);
|
|
#endif
|
|
return;
|
|
} else {
|
|
OutAddr = OutNTE->nte_addr;
|
|
CTEFreeLock(&RouteTableLock, 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)));
|
|
CTEFreeMem(Options);
|
|
#ifdef _PNP_POWER
|
|
DerefIF(IF);
|
|
#endif
|
|
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);
|
|
}
|
|
}
|
|
|
|
// We have the next hop. Now get a forwarding packet.
|
|
if ((NewHeader = GetFWPacket(&Packet, IF, DestType)) !=
|
|
(IPHeader *)NULL) {
|
|
|
|
// Got the header. Fill it in.
|
|
|
|
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;
|
|
|
|
// 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;
|
|
|
|
// Now that we have a packet, go ahead and transfer data the
|
|
// data in if we need to.
|
|
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) {
|
|
SendFWPacket(Packet, Status, DataLength);
|
|
} else {
|
|
// Some sort of failure. Free the packet.
|
|
IPSInfo.ipsi_outdiscards++;
|
|
FreeFWPacket(Packet);
|
|
}
|
|
} else { // Couldn't get a packet, so drop this.
|
|
IPSInfo.ipsi_outdiscards++;
|
|
if (Options)
|
|
CTEFreeMem(Options);
|
|
#ifdef _PNP_POWER
|
|
DerefIF(IF);
|
|
#endif
|
|
}
|
|
} else { // Forward called, but forwarding
|
|
// turned off.
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//* 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;
|
|
|
|
// First, add the route to the address itself. This is a route through
|
|
// the loopback interface.
|
|
|
|
if (AddRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL, LoopNTE->nte_if,
|
|
LOOPBACK_MSS, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL) != IP_SUCCESS)
|
|
return FALSE;
|
|
|
|
Mask = IPNetMask(NTE->nte_addr);
|
|
IF = NTE->nte_if;
|
|
|
|
// 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, &Handle);
|
|
AllSNBCast = (NTE->nte_addr & Mask) | (IF->if_bcast & ~Mask);
|
|
if (LookupRTE(AllSNBCast, NULL_IP_ADDR, HOST_ROUTE_PRI) == NULL) {
|
|
|
|
Status = LockedAddRoute(AllSNBCast, HOST_MASK, IPADDR_LOCAL, IF,
|
|
NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL);
|
|
|
|
} else
|
|
Status = IP_SUCCESS;
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
|
|
if (Status != IP_SUCCESS)
|
|
return FALSE;
|
|
|
|
// If we're doing IGMP, add the route to the multicast address.
|
|
if (IGMPLevel != 0) {
|
|
if (AddRoute(CLASSD_MASK, CLASSD_MASK, IPADDR_LOCAL, NTE->nte_if,
|
|
NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL) != IP_SUCCESS)
|
|
return FALSE;
|
|
}
|
|
|
|
if(NTE->nte_mask != HOST_MASK)
|
|
{
|
|
// And finally the route to the subnet.
|
|
SNMask = NTE->nte_mask;
|
|
if (AddRoute(NTE->nte_addr & SNMask, SNMask, IPADDR_LOCAL, NTE->nte_if,
|
|
NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL) != IP_SUCCESS)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifndef CHICAGO
|
|
#pragma BEGIN_INIT
|
|
#endif
|
|
|
|
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.
|
|
//
|
|
// Returns: TRUE if we succeed, FALSE if we don't.
|
|
//
|
|
uint
|
|
InitNTERouting(NetTableEntry *NTE, uint NumGWs, IPAddr *GWList)
|
|
{
|
|
uint i;
|
|
Interface *IF;
|
|
|
|
CTERefillMem();
|
|
if (NTE != LoopNTE) {
|
|
BCastMinMTU = MIN(BCastMinMTU, NTE->nte_mss);
|
|
|
|
IF = NTE->nte_if;
|
|
AddRoute(IF->if_bcast, HOST_MASK, IPADDR_LOCAL, FirstIF,
|
|
BCastMinMTU, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL);// 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)) {
|
|
AddRoute(NULL_IP_ADDR, DEFAULT_MASK,
|
|
IPADDR_LOCAL, NTE->nte_if, NTE->nte_mss, 1,
|
|
IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL);
|
|
} else
|
|
AddRoute(NULL_IP_ADDR, DEFAULT_MASK,
|
|
net_long(GWList[i]), NTE->nte_if, NTE->nte_mss, 1,
|
|
IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL);
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef CHICAGO
|
|
#pragma BEGIN_INIT
|
|
#endif
|
|
|
|
//* 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)
|
|
{
|
|
int i;
|
|
|
|
CTERefillMem();
|
|
|
|
CTEInitLock(&RouteTableLock);
|
|
|
|
DefGWConfigured = 0;
|
|
DefGWActive = 0;
|
|
|
|
CTEMemSet(&DummyInterface, 0, 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_bcast = IP_LOCAL_BCST;
|
|
DummyInterface.ri_if.if_speed = 10000000;
|
|
DummyInterface.ri_if.if_mtu = 1500;
|
|
DummyInterface.ri_if.if_index = INVALID_IF_INDEX;
|
|
|
|
for (i = 0; i < ROUTE_TABLE_SIZE; i++)
|
|
RouteTable[i] = (RouteTableEntry *)NULL;
|
|
|
|
// 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, NULL);
|
|
// Route for loopback.
|
|
RouterConfigured = (uchar)ci->ici_gateway;
|
|
|
|
CTEInitTimer(&IPRouteTimer);
|
|
|
|
CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL);
|
|
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)
|
|
{
|
|
uint FWBufSize, FWPackets;
|
|
uint FWBufCount;
|
|
NDIS_STATUS Status;
|
|
NDIS_HANDLE BufferPool, FWBufferPool, PacketPool;
|
|
IPHeader *HeaderPtr = NULL;
|
|
uchar *FWBuffer = NULL;
|
|
PNDIS_BUFFER Buffer;
|
|
PNDIS_PACKET Packet;
|
|
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;
|
|
if (RouterConfigured) {
|
|
|
|
|
|
CTERefillMem();
|
|
RtPI = CTEAllocMem(sizeof(ProtInfo));
|
|
if (RtPI == (ProtInfo *)NULL)
|
|
goto failure;
|
|
|
|
RtPI->pi_xmitdone = FWSendComplete;
|
|
|
|
CTEInitLock(&FWPacketFreeLock);
|
|
CTEInitLock(&FWBufFreeLock);
|
|
|
|
MaxFWBufferSize = ci->ici_maxfwbufsize;
|
|
MaxFWPackets = ci->ici_maxfwpackets;
|
|
FWBufSize = MIN(ci->ici_fwbufsize, MaxFWBufferSize);
|
|
FWPackets = MIN(ci->ici_fwpackets, MaxFWPackets);
|
|
|
|
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 = CTEAllocMem(sizeof(RouteSendQ));
|
|
|
|
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;
|
|
|
|
// Round the specified size down to a multiple of our FW buf size.
|
|
CTERefillMem();
|
|
FWBufCount = FWBufSize / FW_BUF_SIZE;
|
|
FWBufSize = FWBufCount * FW_BUF_SIZE;
|
|
|
|
// Allocate the buffers, packets, and memory for our header buffers.
|
|
HeaderPtr = CTEAllocMem(FWPackets * sizeof(IPHeader));
|
|
if (HeaderPtr == (IPHeader *)NULL)
|
|
goto failure;
|
|
|
|
NdisAllocateBufferPool(&Status, &BufferPool, FWPackets);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
goto failure; // Couldn't be a router, fail.
|
|
}
|
|
|
|
NdisAllocatePacketPool(&Status, &PacketPool, FWPackets, sizeof(FWContext));
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
NdisFreeBufferPool(BufferPool);
|
|
goto failure;
|
|
}
|
|
|
|
// Allocate resources for our the buffer pool.
|
|
CTERefillMem();
|
|
FWBuffer = CTEAllocMem(FWBufSize);
|
|
if (FWBuffer == NULL) { // Couldn't get buffer space.
|
|
NdisFreePacketPool(PacketPool);
|
|
NdisFreeBufferPool(BufferPool);
|
|
goto failure;
|
|
}
|
|
|
|
NdisAllocateBufferPool(&Status, &FWBufferPool, FWBufCount);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
NdisFreePacketPool(PacketPool);
|
|
NdisFreeBufferPool(BufferPool);
|
|
goto failure;
|
|
}
|
|
|
|
// Everythings allocated. Put it all together and stick them on the
|
|
// free list.
|
|
for (i = 0; i < FWPackets; i++) {
|
|
FWContext *FWC;
|
|
|
|
NdisAllocateBuffer(&Status, &Buffer, BufferPool, HeaderPtr,
|
|
sizeof(IPHeader));
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
DEBUGCHK;
|
|
NdisAllocatePacket(&Status, &Packet, PacketPool);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
DEBUGCHK;
|
|
|
|
CTEMemSet(Packet->ProtocolReserved, 0, sizeof(FWContext));
|
|
FWC = (FWContext *)Packet->ProtocolReserved;
|
|
FWC->fc_hndisbuff = Buffer;
|
|
FWC->fc_hbuff = HeaderPtr;
|
|
FWC->fc_pc.pc_common.pc_flags = PACKET_FLAG_FW;
|
|
FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP;
|
|
FWC->fc_pc.pc_pi = RtPI;
|
|
FWC->fc_pc.pc_context = Packet;
|
|
|
|
FreeFWPacket(Packet);
|
|
HeaderPtr++;
|
|
}
|
|
|
|
for (i = 0; i < FWBufCount; i++) {
|
|
NdisAllocateBuffer(&Status, &Buffer, FWBufferPool, FWBuffer,
|
|
FW_BUF_SIZE);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
DEBUGCHK;
|
|
|
|
Buffer->Next = FWBufFree; // BUGBUG portability
|
|
FWBufFree = Buffer;
|
|
FWBuffer += FW_BUF_SIZE;
|
|
}
|
|
|
|
CurrentFWPackets = FWPackets;
|
|
CurrentFWBufferSize = FWBufSize;
|
|
|
|
|
|
#if 0
|
|
ForwardBCast = (uchar)ci->ici_fwbcast;
|
|
#else
|
|
ForwardBCast = FALSE;
|
|
#endif
|
|
ForwardPackets = TRUE;
|
|
}
|
|
|
|
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;
|
|
return FALSE;
|
|
|
|
}
|
|
#pragma END_INIT
|