diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/tcpip/ip/iproute.c | 4703 |
1 files changed, 4703 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/ip/iproute.c b/private/ntos/tdi/tcpip/ip/iproute.c new file mode 100644 index 000000000..3a0bb109d --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iproute.c @@ -0,0 +1,4703 @@ +/********************************************************************/ +/** 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 |