summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/ip/iproute.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/tcpip/ip/iproute.c4703
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