mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3509 lines
97 KiB
3509 lines
97 KiB
/********************************************************************/
|
|
/** Microsoft LAN Manager **/
|
|
/** Copyright(c) Microsoft Corp., 1990-1992 **/
|
|
/********************************************************************/
|
|
/* :ts=4 */
|
|
|
|
//*** Init.c - IP VxD init routines.
|
|
//
|
|
// All C init routines are located in this file. We get
|
|
// config. information, allocate structures, and generally get things going.
|
|
|
|
#include "oscfg.h"
|
|
#include "cxport.h"
|
|
#include "ndis.h"
|
|
#include "ip.h"
|
|
#include "ipdef.h"
|
|
#include "ipinit.h"
|
|
#include "llipif.h"
|
|
#include "arp.h"
|
|
#include "info.h"
|
|
#include "iproute.h"
|
|
#include "iprtdef.h"
|
|
#include "ipxmit.h"
|
|
#include "igmp.h"
|
|
#include "icmp.h"
|
|
#include <tdiinfo.h>
|
|
|
|
#ifdef NT
|
|
#include <tdi.h>
|
|
#include <tdikrnl.h>
|
|
#endif
|
|
|
|
|
|
#define NUM_IP_NONHDR_BUFFERS 50
|
|
|
|
#define DEFAULT_RA_TIMEOUT 60
|
|
|
|
#define DEFAULT_ICMP_BUFFERS 5
|
|
|
|
extern IPConfigInfo *IPGetConfig(void);
|
|
extern void IPFreeConfig(IPConfigInfo *);
|
|
extern int IsIPBCast(IPAddr, uchar);
|
|
|
|
extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE *Handle);
|
|
extern void CloseIFConfig(NDIS_HANDLE Handle);
|
|
|
|
// The IPRcv routine.
|
|
extern void IPRcv(void *, void *, uint , uint , NDIS_HANDLE , uint , uint );
|
|
// The transmit complete routine.
|
|
extern void IPSendComplete(void *, PNDIS_PACKET , NDIS_STATUS );
|
|
// Status indication routine.
|
|
extern void IPStatus(void *, NDIS_STATUS, void *, uint);
|
|
// Transfer data complete routine.
|
|
extern void IPTDComplete(void *, PNDIS_PACKET , NDIS_STATUS , uint );
|
|
|
|
extern void IPRcvComplete(void);
|
|
|
|
extern void ICMPInit(uint);
|
|
extern uint IGMPInit(void);
|
|
extern void ICMPTimer(NetTableEntry *);
|
|
extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong);
|
|
extern void TDUserRcv(void *, PNDIS_PACKET, NDIS_STATUS, uint);
|
|
extern void FreeRH(ReassemblyHeader *);
|
|
extern PNDIS_PACKET GrowIPPacketList(void);
|
|
extern PNDIS_BUFFER FreeIPPacket(PNDIS_PACKET Packet);
|
|
|
|
extern ulong GetGMTDelta(void);
|
|
extern ulong GetTime(void);
|
|
extern ulong GetUnique32BitValue(void);
|
|
|
|
extern void NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context,
|
|
ushort IPContext, PVOID *Handle, PNDIS_STRING ConfigName, uint Added);
|
|
|
|
uint IPSetNTEAddr(ushort Context, IPAddr Addr, IPMask Mask, SetAddrControl *ControlBlock, SetAddrRtn Rtn);
|
|
|
|
extern NDIS_HANDLE BufferPool;
|
|
EXTERNAL_LOCK(HeaderLock)
|
|
#ifdef NT
|
|
extern SLIST_HEADER PacketList;
|
|
extern SLIST_HEADER HdrBufList;
|
|
#endif
|
|
|
|
extern NetTableEntry *LoopNTE;
|
|
|
|
extern uchar RouterConfigured;
|
|
|
|
//NetTableEntry *NetTable; // Pointer to the net table.
|
|
|
|
NetTableEntry *NetTableList; // List of NTEs.
|
|
int NumNTE; // Number of NTEs.
|
|
|
|
AddrTypeCache ATCache[ATC_SIZE];
|
|
uint ATCIndex;
|
|
|
|
uchar RATimeout; // Number of seconds to time out a
|
|
// reassembly.
|
|
ushort NextNTEContext; // Next NTE context to use.
|
|
|
|
#if 0
|
|
DEFINE_LOCK_STRUCTURE(PILock)
|
|
#endif
|
|
|
|
ProtInfo IPProtInfo[MAX_IP_PROT]; // Protocol information table.
|
|
ProtInfo *LastPI; // Last protinfo structure looked at.
|
|
int NextPI; // Next PI field to be used.
|
|
ProtInfo *RawPI = NULL; // Raw IP protinfo
|
|
|
|
ulong TimeStamp;
|
|
ulong TSFlag;
|
|
|
|
uint DefaultTTL;
|
|
uint DefaultTOS;
|
|
uchar TrRii = TR_RII_ALL;
|
|
|
|
// Interface *IFTable[MAX_IP_NETS];
|
|
Interface *IFList; // List of interfaces active.
|
|
Interface *FirstIF; // First 'real' IF.
|
|
ulong NumIF;
|
|
|
|
#ifdef _PNP_POWER
|
|
#define BITS_PER_WORD 32
|
|
ulong IFBitMask[(MAX_TDI_ENTITIES / BITS_PER_WORD) + 1];
|
|
#endif // _PNP_POWER
|
|
|
|
IPSNMPInfo IPSInfo;
|
|
uint DHCPActivityCount = 0;
|
|
uint IGMPLevel;
|
|
|
|
#ifdef NT
|
|
|
|
#ifndef _PNP_POWER
|
|
|
|
extern NameMapping *AdptNameTable;
|
|
extern DriverRegMapping *DriverNameTable;
|
|
|
|
#endif // _PNP_POWER
|
|
|
|
VOID
|
|
SetPersistentRoutesForNTE(
|
|
IPAddr Address,
|
|
IPMask Mask,
|
|
ULONG IFIndex
|
|
);
|
|
|
|
#else // NT
|
|
|
|
#ifndef _PNP_POWER
|
|
|
|
extern NameMapping AdptNameTable[];
|
|
extern DriverRegMapping DriverNameTable[];
|
|
|
|
#endif // _PNP_POWER
|
|
|
|
#endif // NT
|
|
|
|
#ifndef _PNP_POWER
|
|
extern uint NumRegDrivers;
|
|
uint MaxIPNets = 0;
|
|
#endif // _PNP_POWER
|
|
|
|
uint InterfaceSize; // Size of a net interface.
|
|
NetTableEntry *DHCPNTE = NULL;
|
|
|
|
|
|
#ifdef NT
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
//
|
|
// Make init code disposable.
|
|
//
|
|
void InitTimestamp();
|
|
int InitNTE(NetTableEntry *NTE);
|
|
int InitInterface(NetTableEntry *NTE);
|
|
LLIPRegRtn GetLLRegPtr(PNDIS_STRING Name);
|
|
LLIPRegRtn FindRegPtr(PNDIS_STRING Name);
|
|
uint IPRegisterDriver(PNDIS_STRING Name, LLIPRegRtn Ptr);
|
|
void CleanAdaptTable();
|
|
void OpenAdapters();
|
|
int IPInit();
|
|
|
|
#if 0 // BUGBUG: These can eventually be made init time only.
|
|
|
|
#pragma alloc_text(INIT, IPGetInfo)
|
|
#pragma alloc_text(INIT, IPTimeout)
|
|
|
|
#endif // 0
|
|
|
|
#pragma alloc_text(INIT, InitTimestamp)
|
|
#ifndef _PNP_POWER
|
|
#pragma alloc_text(INIT, InitNTE)
|
|
#pragma alloc_text(INIT, InitInterface)
|
|
#endif
|
|
#pragma alloc_text(INIT, CleanAdaptTable)
|
|
#pragma alloc_text(INIT, OpenAdapters)
|
|
#pragma alloc_text(INIT, IPRegisterDriver)
|
|
#pragma alloc_text(INIT, GetLLRegPtr)
|
|
#pragma alloc_text(INIT, FindRegPtr)
|
|
#pragma alloc_text(INIT, IPInit)
|
|
|
|
|
|
//
|
|
// Pagable code
|
|
//
|
|
uint
|
|
IPAddDynamicNTE(ushort InterfaceContext, IPAddr NewAddr, IPMask NewMask,
|
|
ushort *NTEContext, ulong *NTEInstance);
|
|
|
|
#pragma alloc_text(PAGE, IPAddDynamicNTE)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
extern PDRIVER_OBJECT IPDriverObject;
|
|
|
|
NTSTATUS
|
|
SetRegDWORDValue(
|
|
HANDLE KeyHandle,
|
|
PWCHAR ValueName,
|
|
PULONG ValueData
|
|
);
|
|
|
|
//
|
|
// Debugging macros
|
|
//
|
|
#if DBG
|
|
|
|
#define TCPTRACE(many_args) DbgPrint many_args
|
|
|
|
#else // DBG
|
|
|
|
#define TCPTRACE(many_args)
|
|
|
|
#endif // DBG
|
|
|
|
|
|
// SetIFContext - Set the context on a particular interface.
|
|
//
|
|
// A routine to set the filter context on a particular interface.
|
|
//
|
|
// Input: Index - Interface index of i/f to be set.
|
|
// Context - Context to set.
|
|
//
|
|
// Returns: Status of attempt.
|
|
//
|
|
IP_STATUS
|
|
SetIFContext(uint Index, INTERFACE_CONTEXT *Context)
|
|
{
|
|
Interface *IF;
|
|
|
|
// Walk the list, looking for a matching index.
|
|
for (IF = IFList; IF != NULL; IF = IF->if_next) {
|
|
if (IF->if_index == Index) {
|
|
IF->if_filtercontext = Context;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we found one, return success. Otherwise fail.
|
|
if (IF != NULL) {
|
|
return IP_SUCCESS;
|
|
} else {
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
}
|
|
|
|
// SetFilterPtr - A routine to set the filter pointer.
|
|
//
|
|
// This routine sets the IP forwarding filter callout.
|
|
//
|
|
// Input: FilterPtr - Pointer to routine to call when filtering. May
|
|
// be NULL.
|
|
//
|
|
// Returns: IP_SUCCESS.
|
|
//
|
|
IP_STATUS
|
|
SetFilterPtr(IPPacketFilterPtr FilterPtr)
|
|
{
|
|
Interface *IF;
|
|
|
|
//
|
|
// If the pointer is being set to NULL, means filtering is
|
|
// being turned off. Remove all the contexts we have
|
|
//
|
|
|
|
if(FilterPtr == NULL)
|
|
{
|
|
|
|
for (IF = IFList; IF != NULL; IF = IF->if_next)
|
|
{
|
|
IF->if_filtercontext = NULL;
|
|
}
|
|
}
|
|
|
|
ForwardFilterPtr = FilterPtr;
|
|
|
|
return IP_SUCCESS;
|
|
}
|
|
|
|
// SetMapRoutePtr - A routine to set the dial on demand callout pointer.
|
|
//
|
|
// This routine sets the IP dial on demand callout.
|
|
//
|
|
// Input: MapRoutePtr - Pointer to routine to call when we need to bring
|
|
// up a link. May be NULL
|
|
//
|
|
// Returns: IP_SUCCESS.
|
|
//
|
|
IP_STATUS
|
|
SetMapRoutePtr(IPMapRouteToInterfacePtr MapRoutePtr)
|
|
{
|
|
DODCallout = MapRoutePtr;
|
|
return IP_SUCCESS;
|
|
}
|
|
|
|
#endif // NT
|
|
|
|
|
|
//** SetDHCPNTE
|
|
//
|
|
// Routine to identify which NTE is currently being DHCP'ed. We take as input
|
|
// an nte_context. If the context is less than the max NTE context, we look
|
|
// for a matching NTE and if we find him we save a pointer. If we don't we
|
|
// fail. If the context > max NTE context we're disabling DHCPing, and
|
|
// we NULL out the save pointer.
|
|
//
|
|
// Input: Context - NTE context value.
|
|
//
|
|
// Returns: TRUE if we succeed, FALSE if we don't.
|
|
//
|
|
uint
|
|
SetDHCPNTE(uint Context)
|
|
{
|
|
CTELockHandle Handle;
|
|
NetTableEntry *NTE;
|
|
ushort NTEContext;
|
|
uint RetCode;
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
if (Context <= 0xffff) {
|
|
// We're setting the DHCP NTE. Look for one matching the context.
|
|
|
|
NTEContext = (ushort)Context;
|
|
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if (NTE != LoopNTE && NTE->nte_context == NTEContext) {
|
|
// Found one. Save it and break out.
|
|
DHCPNTE = NTE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RetCode = (NTE != NULL);
|
|
} else {
|
|
// The context is invalid, so we're deleting the DHCP NTE.
|
|
DHCPNTE = NULL;
|
|
RetCode = TRUE;
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
|
|
return RetCode;
|
|
}
|
|
|
|
|
|
//** SetDHCPNTE
|
|
//
|
|
// Routine for upper layers to call to check if the IPContext value passed
|
|
// up to a RcvHandler identifies an interface that is currently being
|
|
// DHCP'd.
|
|
//
|
|
// Input: Context - Pointer to an NTE
|
|
//
|
|
// Returns: TRUE if we succeed, FALSE if we don't.
|
|
//
|
|
uint
|
|
IsDHCPInterface(void *IPContext)
|
|
{
|
|
// CTELockHandle Handle;
|
|
uint RetCode;
|
|
NetTableEntry *NTE = (NetTableEntry *) IPContext;
|
|
|
|
|
|
// CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
if (DHCPNTE == NTE) {
|
|
RetCode = TRUE;
|
|
}
|
|
else {
|
|
RetCode = FALSE;
|
|
}
|
|
|
|
// CTEFreeLock(&RouteTableLock, Handle);
|
|
|
|
return(RetCode);
|
|
}
|
|
|
|
|
|
//** CloseNets - Close active nets.
|
|
//
|
|
// Called when we need to close some lower layer interfaces.
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
void
|
|
CloseNets(void)
|
|
{
|
|
NetTableEntry *nt;
|
|
|
|
for (nt = NetTableList; nt != NULL; nt = nt->nte_next)
|
|
(*nt->nte_if->if_close)(nt->nte_if->if_lcontext); // Call close routine for this net.
|
|
}
|
|
|
|
//** IPRegisterProtocol - Register a protocol with IP.
|
|
//
|
|
// Called by upper layer software to register a protocol. The UL supplies
|
|
// pointers to receive routines and a protocol value to be used on xmits/receives.
|
|
//
|
|
// Entry:
|
|
// Protocol - Protocol value to be returned.
|
|
// RcvHandler - Receive handler to be called when frames for Protocol are received.
|
|
// XmitHandler - Xmit. complete handler to be called when frames from Protocol are completed.
|
|
// StatusHandler - Handler to be called when status indication is to be delivered.
|
|
//
|
|
// Returns:
|
|
// Pointer to ProtInfo,
|
|
//
|
|
void *
|
|
IPRegisterProtocol(uchar Protocol, void *RcvHandler, void *XmitHandler,
|
|
void *StatusHandler, void *RcvCmpltHandler)
|
|
{
|
|
ProtInfo *PI = (ProtInfo *)NULL;
|
|
int i;
|
|
int Incr;
|
|
#if 0
|
|
CTELockHandle Handle;
|
|
|
|
|
|
CTEGetLock(&PILock, &Handle);
|
|
#endif
|
|
|
|
// First check to see if it's already registered. If it is just replace it.
|
|
for (i = 0; i < NextPI; i++)
|
|
if (IPProtInfo[i].pi_protocol == Protocol) {
|
|
PI = &IPProtInfo[i];
|
|
Incr = 0;
|
|
break;
|
|
}
|
|
|
|
if (PI == (ProtInfo *)NULL) {
|
|
if (NextPI >= MAX_IP_PROT) {
|
|
#if 0
|
|
CTEFreeLock(&PILock, Handle);
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
PI = &IPProtInfo[NextPI];
|
|
Incr = 1;
|
|
|
|
if (Protocol == PROTOCOL_ANY) {
|
|
RawPI = PI;
|
|
}
|
|
}
|
|
|
|
PI->pi_protocol = Protocol;
|
|
PI->pi_rcv = RcvHandler;
|
|
PI->pi_xmitdone = XmitHandler;
|
|
PI->pi_status = StatusHandler;
|
|
PI->pi_rcvcmplt = RcvCmpltHandler;
|
|
NextPI += Incr;
|
|
|
|
#if 0
|
|
CTEFreeLock(&PILock, Handle);
|
|
#endif
|
|
|
|
#ifndef _PNP_POWER
|
|
#ifdef SECFLTR
|
|
|
|
//
|
|
// If this was a registration, call the status routine of each protocol
|
|
// to inform it of all existing interfaces. Yes, this is a hack, but
|
|
// it will work until PnP is turned on in NT.
|
|
//
|
|
// It is assumed that none of the upper layer status routines call back
|
|
// into IP.
|
|
//
|
|
// Note that we don't hold any locks here since no one manipulates the
|
|
// NTE list in a non-PNP build during the init phase.
|
|
//
|
|
if (StatusHandler != NULL) {
|
|
NetTableEntry *NTE;
|
|
NDIS_HANDLE ConfigHandle = NULL;
|
|
int i;
|
|
|
|
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if ( !(IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) &&
|
|
!(IP_LOOPBACK_ADDR(NTE->nte_addr))
|
|
)
|
|
{
|
|
//
|
|
// Open a configuration key
|
|
//
|
|
if (!OpenIFConfig(&(NTE->nte_if->if_configname), &ConfigHandle))
|
|
{
|
|
//
|
|
// Not much we can do. The transports will have
|
|
// to handle this.
|
|
//
|
|
CTEAssert(ConfigHandle == NULL);
|
|
}
|
|
|
|
(* ((ULStatusProc) StatusHandler))(IP_HW_STATUS, IP_ADDR_ADDED,
|
|
NTE->nte_addr, NULL_IP_ADDR, NULL_IP_ADDR, 0, ConfigHandle );
|
|
|
|
if (ConfigHandle != NULL) {
|
|
CloseIFConfig(ConfigHandle);
|
|
ConfigHandle = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // SECFLTR
|
|
#endif // _PNP_POWER
|
|
|
|
return PI;
|
|
}
|
|
|
|
//** IPSetMCastAddr - Set/Delete a multicast address.
|
|
//
|
|
// Called by an upper layer protocol or client to set or delete an IP multicast
|
|
// address.
|
|
//
|
|
// Input: Address - Address to be set/deleted.
|
|
// IF - IP Address of interface to set/delete on.
|
|
// Action - TRUE if we're setting, FALSE if we're deleting.
|
|
//
|
|
// Returns: IP_STATUS of set/delete attempt.
|
|
//
|
|
IP_STATUS
|
|
IPSetMCastAddr(IPAddr Address, IPAddr IF, uint Action)
|
|
{
|
|
NetTableEntry *LocalNTE;
|
|
|
|
// Don't let him do this on the loopback address, since we don't have a
|
|
// route table entry for class D address on the loopback interface and
|
|
// we don't want a packet with a loopback source address to show up on
|
|
// the wire.
|
|
if (IP_LOOPBACK_ADDR(IF))
|
|
return IP_BAD_REQ;
|
|
|
|
for (LocalNTE = NetTableList; LocalNTE != NULL;
|
|
LocalNTE = LocalNTE->nte_next) {
|
|
if (LocalNTE != LoopNTE && ((LocalNTE->nte_flags & NTE_VALID) &&
|
|
(IP_ADDR_EQUAL(IF, NULL_IP_ADDR) ||
|
|
IP_ADDR_EQUAL(IF, LocalNTE->nte_addr))))
|
|
break;
|
|
}
|
|
|
|
if (LocalNTE == NULL) {
|
|
// Couldn't find a matching NTE.
|
|
return IP_BAD_REQ;
|
|
}
|
|
|
|
return IGMPAddrChange(LocalNTE, Address, Action ? IGMP_ADD : IGMP_DELETE);
|
|
|
|
|
|
}
|
|
|
|
//** IPGetAddrType - Return the type of a address.
|
|
//
|
|
// Called by the upper layer to determine the type of a remote address.
|
|
//
|
|
// Input: Address - The address in question.
|
|
//
|
|
// Returns: The DEST type of the address.
|
|
//
|
|
uchar
|
|
IPGetAddrType(IPAddr Address)
|
|
{
|
|
return GetAddrType(Address);
|
|
}
|
|
|
|
//** IPGetLocalMTU - Return the MTU for a local address
|
|
//
|
|
// Called by the upper layer to get the local MTU for a local address.
|
|
//
|
|
// Input: LocalAddr - Local address in question.
|
|
// MTU - Where to return the local MTU.
|
|
//
|
|
// Returns: TRUE if we found the MTU, FALSE otherwise.
|
|
//
|
|
uchar
|
|
IPGetLocalMTU(IPAddr LocalAddr, ushort *MTU)
|
|
{
|
|
NetTableEntry *NTE;
|
|
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if (IP_ADDR_EQUAL(NTE->nte_addr, LocalAddr) &&
|
|
(NTE->nte_flags & NTE_VALID)) {
|
|
*MTU = NTE->nte_mss;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// Special case in case the local address is a loopback address other than
|
|
// 127.0.0.1.
|
|
if (IP_LOOPBACK_ADDR(LocalAddr)) {
|
|
*MTU = LoopNTE->nte_mss;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//** IPUpdateRcvdOptions - Update options for use in replying.
|
|
//
|
|
// A routine to update options for use in a reply. We reverse any source route options,
|
|
// and optionally update the record route option. We also return the index into the
|
|
// options of the record route options (if we find one). The options are assumed to be
|
|
// correct - no validation is performed on them. We fill in the caller provided
|
|
// IPOptInfo with the new option buffer.
|
|
//
|
|
// Input: Options - Pointer to option info structure with buffer to be reversed.
|
|
// NewOptions - Pointer to option info structure to be filled in.
|
|
// Src - Source address of datagram that generated the options.
|
|
// LocalAddr - Local address responding. If this != NULL_IP_ADDR, then
|
|
// record route and timestamp options will be updated with this
|
|
// address.
|
|
//
|
|
//
|
|
// Returns: Index into options of record route option, if any.
|
|
//
|
|
IP_STATUS
|
|
IPUpdateRcvdOptions(IPOptInfo *OldOptions, IPOptInfo *NewOptions, IPAddr Src, IPAddr LocalAddr)
|
|
{
|
|
uchar Length, Ptr;
|
|
uchar i; // Index variable
|
|
IPAddr UNALIGNED *LastAddr; // First address in route.
|
|
IPAddr UNALIGNED *FirstAddr; // Last address in route.
|
|
IPAddr TempAddr; // Temp used in exchange.
|
|
uchar *Options, OptLength;
|
|
OptIndex Index; // Optindex used by UpdateOptions.
|
|
|
|
Options = CTEAllocMem(OptLength = OldOptions->ioi_optlength);
|
|
|
|
if (!Options)
|
|
return IP_NO_RESOURCES;
|
|
|
|
CTEMemCopy(Options, OldOptions->ioi_options, OptLength);
|
|
Index.oi_srindex = MAX_OPT_SIZE;
|
|
Index.oi_rrindex = MAX_OPT_SIZE;
|
|
Index.oi_tsindex = MAX_OPT_SIZE;
|
|
|
|
NewOptions->ioi_flags &= ~IP_FLAG_SSRR;
|
|
|
|
i = 0;
|
|
while(i < OptLength) {
|
|
if (Options[i] == IP_OPT_EOL)
|
|
break;
|
|
|
|
if (Options[i] == IP_OPT_NOP) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
Length = Options[i+IP_OPT_LENGTH];
|
|
switch (Options[i]) {
|
|
case IP_OPT_SSRR:
|
|
NewOptions->ioi_flags |= IP_FLAG_SSRR;
|
|
case IP_OPT_LSRR:
|
|
// Have a source route. We save the last gateway we came through as
|
|
// the new address, reverse the list, shift the list forward one address,
|
|
// and set the Src address as the last gateway in the list.
|
|
|
|
// First, check for an empty source route. If the SR is empty
|
|
// we'll skip most of this.
|
|
if (Length != (MIN_RT_PTR - 1)) {
|
|
// A non empty source route.
|
|
// First reverse the list in place.
|
|
Ptr = Options[i+IP_OPT_PTR] - 1 - sizeof(IPAddr);
|
|
LastAddr = (IPAddr *)(&Options[i + Ptr]);
|
|
FirstAddr = (IPAddr *)(&Options[i + IP_OPT_PTR + 1]);
|
|
NewOptions->ioi_addr = *LastAddr; // Save Last address as
|
|
// first hop of new route.
|
|
while (LastAddr > FirstAddr) {
|
|
TempAddr = *LastAddr;
|
|
*LastAddr-- = *FirstAddr;
|
|
*FirstAddr++ = TempAddr;
|
|
}
|
|
|
|
// Shift the list forward one address. We'll copy all but
|
|
// one IP address.
|
|
CTEMemCopy(&Options[i + IP_OPT_PTR + 1],
|
|
&Options[i + IP_OPT_PTR + 1 + sizeof(IPAddr)],
|
|
Length - (sizeof(IPAddr) + (MIN_RT_PTR -1)));
|
|
|
|
// Set source as last address of route.
|
|
*(IPAddr UNALIGNED *)(&Options[i + Ptr]) = Src;
|
|
}
|
|
|
|
Options[i+IP_OPT_PTR] = MIN_RT_PTR; // Set pointer to min legal value.
|
|
i += Length;
|
|
break;
|
|
case IP_OPT_RR:
|
|
// Save the index in case LocalAddr is specified. If it isn't specified,
|
|
// reset the pointer and zero the option.
|
|
Index.oi_rrindex = i;
|
|
if (LocalAddr == NULL_IP_ADDR) {
|
|
CTEMemSet(&Options[i+MIN_RT_PTR-1], 0, Length - (MIN_RT_PTR-1));
|
|
Options[i+IP_OPT_PTR] = MIN_RT_PTR;
|
|
}
|
|
i += Length;
|
|
break;
|
|
case IP_OPT_TS:
|
|
Index.oi_tsindex = i;
|
|
|
|
// We have a timestamp option. If we're not going to update, reinitialize
|
|
// it for next time. For the 'unspecified' options, just zero the buffer.
|
|
// For the 'specified' options, we need to zero the timestamps without
|
|
// zeroing the specified addresses.
|
|
if (LocalAddr == NULL_IP_ADDR) { // Not going to update, reinitialize.
|
|
uchar Flags;
|
|
|
|
Options[i+IP_OPT_PTR] = MIN_TS_PTR; // Reinitialize pointer.
|
|
Flags = Options[i+IP_TS_OVFLAGS] & IP_TS_FLMASK; // Get option type.
|
|
Options[i+IP_TS_OVFLAGS] = Flags; // Clear overflow count.
|
|
switch (Flags) {
|
|
uchar j;
|
|
ulong UNALIGNED *TSPtr;
|
|
|
|
// The unspecified types. Just clear the buffer.
|
|
case TS_REC_TS:
|
|
case TS_REC_ADDR:
|
|
CTEMemSet(&Options[i+MIN_TS_PTR-1], 0, Length - (MIN_TS_PTR-1));
|
|
break;
|
|
|
|
// We have a list of addresses specified. Just clear the timestamps.
|
|
case TS_REC_SPEC:
|
|
// j starts off as the offset in bytes from start of buffer to
|
|
// first timestamp.
|
|
j = MIN_TS_PTR-1+sizeof(IPAddr);
|
|
// TSPtr points at timestamp.
|
|
TSPtr = (ulong UNALIGNED *)&Options[i+j];
|
|
|
|
// Now j is offset of end of timestamp being zeroed.
|
|
j += sizeof(ulong);
|
|
while (j <= Length) {
|
|
*TSPtr++ = 0;
|
|
j += sizeof(ulong);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
i += Length;
|
|
break;
|
|
|
|
default:
|
|
i += Length;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (LocalAddr != NULL_IP_ADDR) {
|
|
UpdateOptions(Options, &Index, LocalAddr);
|
|
}
|
|
|
|
NewOptions->ioi_optlength = OptLength;
|
|
NewOptions->ioi_options = Options;
|
|
return IP_SUCCESS;
|
|
|
|
}
|
|
|
|
//* ValidRouteOption - Validate a source or record route option.
|
|
//
|
|
// Called to validate that a user provided source or record route option is good.
|
|
//
|
|
// Entry: Option - Pointer to option to be checked.
|
|
// NumAddr - NumAddr that need to fit in option.
|
|
// BufSize - Maximum size of option.
|
|
//
|
|
// Returns: 1 if option is good, 0 if not.
|
|
//
|
|
uchar
|
|
ValidRouteOption(uchar *Option, uint NumAddr, uint BufSize)
|
|
{
|
|
if (Option[IP_OPT_LENGTH] < (3 + (sizeof(IPAddr)*NumAddr)) ||
|
|
Option[IP_OPT_LENGTH] > BufSize ||
|
|
((Option[IP_OPT_LENGTH] - 3) % sizeof(IPAddr))) // Routing options is too small.
|
|
return 0;
|
|
|
|
if (Option[IP_OPT_PTR] != MIN_RT_PTR) // Pointer isn't correct.
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
//** IPInitOptions - Initialize an option buffer.
|
|
//
|
|
// Called by an upper layer routine to initialize an option buffer. We fill
|
|
// in the default values for TTL, TOS, and flags, and NULL out the options
|
|
// buffer and size.
|
|
//
|
|
// Input: Options - Pointer to IPOptInfo structure.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
IPInitOptions(IPOptInfo *Options)
|
|
{
|
|
Options->ioi_addr = NULL_IP_ADDR;
|
|
|
|
Options->ioi_ttl = (uchar)DefaultTTL;
|
|
Options->ioi_tos = (uchar)DefaultTOS;
|
|
Options->ioi_flags = 0;
|
|
|
|
Options->ioi_options = (uchar *)NULL;
|
|
Options->ioi_optlength = 0;
|
|
|
|
}
|
|
|
|
//** IPCopyOptions - Copy the user's options into IP header format.
|
|
//
|
|
// This routine takes an option buffer supplied by an IP client, validates it, and
|
|
// creates an IPOptInfo structure that can be passed to the IP layer for transmission. This
|
|
// includes allocating a buffer for the options, munging any source route
|
|
// information into the real IP format.
|
|
//
|
|
// Note that we never lock this structure while we're using it. This may cause transitory
|
|
// incosistencies while the structure is being updated if it is in use during the update.
|
|
// This shouldn't be a problem - a packet or too might get misrouted, but it should
|
|
// straighten itself out quickly. If this is a problem the client should make sure not
|
|
// to call this routine while it's in the IPTransmit routine.
|
|
//
|
|
// Entry: Options - Pointer to buffer of user supplied options.
|
|
// Size - Size in bytes of option buffer
|
|
// OptInfoPtr - Pointer to IPOptInfo structure to be filled in.
|
|
//
|
|
// Returns: A status, indicating whether or not the options were valid and copied.
|
|
//
|
|
IP_STATUS
|
|
IPCopyOptions(uchar *Options, uint Size, IPOptInfo *OptInfoPtr)
|
|
{
|
|
uchar *TempOptions; // Buffer of options we'll build
|
|
uint TempSize; // Size of options.
|
|
IP_STATUS TempStatus; // Temporary status
|
|
uchar OptSeen = 0; // Indicates which options we've seen.
|
|
|
|
|
|
OptInfoPtr->ioi_addr = NULL_IP_ADDR;
|
|
|
|
OptInfoPtr->ioi_flags &= ~IP_FLAG_SSRR;
|
|
|
|
if (Size == 0) {
|
|
CTEAssert(FALSE);
|
|
OptInfoPtr->ioi_options = (uchar *)NULL;
|
|
OptInfoPtr->ioi_optlength = 0;
|
|
return IP_SUCCESS;
|
|
}
|
|
|
|
|
|
// Option size needs to be rounded to multiple of 4.
|
|
if ((TempOptions = CTEAllocMem(((Size & 3) ? (Size & ~3) + 4 : Size))) == (uchar *)NULL)
|
|
return IP_NO_RESOURCES; // Couldn't get a buffer, return error.
|
|
|
|
CTEMemSet(TempOptions, 0, ((Size & 3) ? (Size & ~3) + 4 : Size));
|
|
|
|
// OK, we have a buffer. Loop through the provided buffer, copying options.
|
|
TempSize = 0;
|
|
TempStatus = IP_PENDING;
|
|
while (Size && TempStatus == IP_PENDING) {
|
|
uint SRSize; // Size of a source route option.
|
|
|
|
switch (*Options) {
|
|
case IP_OPT_EOL:
|
|
TempStatus = IP_SUCCESS;
|
|
break;
|
|
case IP_OPT_NOP:
|
|
TempOptions[TempSize++] = *Options++;
|
|
Size--;
|
|
break;
|
|
case IP_OPT_SSRR:
|
|
if (OptSeen & (OPT_LSRR | OPT_SSRR)) {
|
|
TempStatus = IP_BAD_OPTION; // We've already seen a record route.
|
|
break;
|
|
}
|
|
OptInfoPtr->ioi_flags |= IP_FLAG_SSRR;
|
|
OptSeen |= OPT_SSRR; // Fall through to LSRR code.
|
|
case IP_OPT_LSRR:
|
|
if ( (*Options == IP_OPT_LSRR) &&
|
|
(OptSeen & (OPT_LSRR | OPT_SSRR))
|
|
) {
|
|
TempStatus = IP_BAD_OPTION; // We've already seen a record route.
|
|
break;
|
|
}
|
|
if (*Options == IP_OPT_LSRR)
|
|
OptSeen |= OPT_LSRR;
|
|
if (!ValidRouteOption(Options, 2, Size)) {
|
|
TempStatus = IP_BAD_OPTION;
|
|
break;
|
|
}
|
|
|
|
// Option is valid. Copy the first hop address to NewAddr, and move all
|
|
// of the other addresses forward.
|
|
TempOptions[TempSize++] = *Options++; // Copy option type.
|
|
SRSize = *Options++;
|
|
Size -= SRSize;
|
|
SRSize -= sizeof(IPAddr);
|
|
TempOptions[TempSize++] = SRSize;
|
|
TempOptions[TempSize++] = *Options++; // Copy pointer.
|
|
OptInfoPtr->ioi_addr = *(IPAddr UNALIGNED *)Options;
|
|
Options += sizeof(IPAddr); // Point to address beyond first hop.
|
|
CTEMemCopy(&TempOptions[TempSize], Options, SRSize - 3);
|
|
TempSize += (SRSize - 3);
|
|
Options += (SRSize - 3);
|
|
break;
|
|
case IP_OPT_RR:
|
|
if (OptSeen & OPT_RR) {
|
|
TempStatus = IP_BAD_OPTION; // We've already seen a record route.
|
|
break;
|
|
}
|
|
OptSeen |= OPT_RR;
|
|
if (!ValidRouteOption(Options, 1, Size)) {
|
|
TempStatus = IP_BAD_OPTION;
|
|
break;
|
|
}
|
|
SRSize = Options[IP_OPT_LENGTH];
|
|
CTEMemCopy(&TempOptions[TempSize], Options, SRSize);
|
|
TempSize += SRSize;
|
|
Options += SRSize;
|
|
Size -= SRSize;
|
|
break;
|
|
case IP_OPT_TS:
|
|
{
|
|
uchar Overflow, Flags;
|
|
|
|
if (OptSeen & OPT_TS) {
|
|
TempStatus = IP_BAD_OPTION; // We've already seen a time stamp
|
|
break;
|
|
}
|
|
OptSeen |= OPT_TS;
|
|
Flags = Options[IP_TS_OVFLAGS] & IP_TS_FLMASK;
|
|
Overflow = (Options[IP_TS_OVFLAGS] & IP_TS_OVMASK) >> 4;
|
|
|
|
if (Overflow || (Flags != TS_REC_TS && Flags != TS_REC_ADDR &&
|
|
Flags != TS_REC_SPEC)) {
|
|
TempStatus = IP_BAD_OPTION; // Bad flags or overflow value.
|
|
break;
|
|
}
|
|
|
|
SRSize = Options[IP_OPT_LENGTH];
|
|
if (SRSize > Size || SRSize < 8 ||
|
|
Options[IP_OPT_PTR] != MIN_TS_PTR) {
|
|
TempStatus = IP_BAD_OPTION; // Option size isn't good.
|
|
break;
|
|
}
|
|
CTEMemCopy(&TempOptions[TempSize], Options, SRSize);
|
|
TempSize += SRSize;
|
|
Options += SRSize;
|
|
Size -= SRSize;
|
|
}
|
|
break;
|
|
default:
|
|
TempStatus = IP_BAD_OPTION; // Unknown option, error.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (TempStatus == IP_PENDING) // We broke because we hit the end of the buffer.
|
|
TempStatus = IP_SUCCESS; // that's OK.
|
|
|
|
if (TempStatus != IP_SUCCESS) { // We had some sort of an error.
|
|
CTEFreeMem(TempOptions);
|
|
return TempStatus;
|
|
}
|
|
|
|
// Check the option size here to see if it's too big. We check it here at the end
|
|
// instead of at the start because the option size may shrink if there are source route
|
|
// options, and we don't want to accidentally error out a valid option.
|
|
TempSize = (TempSize & 3 ? (TempSize & ~3) + 4 : TempSize);
|
|
if (TempSize > MAX_OPT_SIZE) {
|
|
CTEFreeMem(TempOptions);
|
|
return IP_OPTION_TOO_BIG;
|
|
}
|
|
OptInfoPtr->ioi_options = TempOptions;
|
|
OptInfoPtr->ioi_optlength = TempSize;
|
|
|
|
return IP_SUCCESS;
|
|
|
|
}
|
|
|
|
//** IPFreeOptions - Free options we're done with.
|
|
//
|
|
// Called by the upper layer when we're done with options. All we need to do is free
|
|
// the options.
|
|
//
|
|
// Input: OptInfoPtr - Pointer to IPOptInfo structure to be freed.
|
|
//
|
|
// Returns: Status of attempt to free options.
|
|
//
|
|
IP_STATUS
|
|
IPFreeOptions(IPOptInfo *OptInfoPtr)
|
|
{
|
|
if (OptInfoPtr->ioi_options) {
|
|
// We have options to free. Save the pointer and zero the structure field before
|
|
// freeing the memory to try and present race conditions with it's use.
|
|
uchar *TempPtr = OptInfoPtr->ioi_options;
|
|
|
|
OptInfoPtr->ioi_options = (uchar *)NULL;
|
|
CTEFreeMem(TempPtr);
|
|
OptInfoPtr->ioi_optlength = 0;
|
|
OptInfoPtr->ioi_addr = NULL_IP_ADDR;
|
|
OptInfoPtr->ioi_flags &= ~IP_FLAG_SSRR;
|
|
}
|
|
return IP_SUCCESS;
|
|
}
|
|
|
|
|
|
//BUGBUG - After we're done testing, move BEGIN_INIT up here.
|
|
|
|
//** ipgetinfo - Return pointers to our NetInfo structures.
|
|
//
|
|
// Called by upper layer software during init. time. The caller
|
|
// passes a buffer, which we fill in with pointers to NetInfo
|
|
// structures.
|
|
//
|
|
// Entry:
|
|
// Buffer - Pointer to buffer to be filled in.
|
|
// Size - Size in bytes of buffer.
|
|
//
|
|
// Returns:
|
|
// Status of command.
|
|
//
|
|
IP_STATUS
|
|
IPGetInfo(IPInfo *Buffer, int Size)
|
|
{
|
|
if (Size < sizeof(IPInfo))
|
|
return IP_BUF_TOO_SMALL; // Not enough buffer space.
|
|
|
|
Buffer->ipi_version = IP_DRIVER_VERSION;
|
|
Buffer->ipi_hsize = sizeof(IPHeader);
|
|
Buffer->ipi_xmit = IPTransmit;
|
|
Buffer->ipi_protreg = IPRegisterProtocol;
|
|
Buffer->ipi_openrce = OpenRCE;
|
|
Buffer->ipi_closerce = CloseRCE;
|
|
Buffer->ipi_getaddrtype = IPGetAddrType;
|
|
Buffer->ipi_getlocalmtu = IPGetLocalMTU;
|
|
Buffer->ipi_getpinfo = IPGetPInfo;
|
|
Buffer->ipi_checkroute = IPCheckRoute;
|
|
Buffer->ipi_initopts = IPInitOptions;
|
|
Buffer->ipi_updateopts = IPUpdateRcvdOptions;
|
|
Buffer->ipi_copyopts = IPCopyOptions;
|
|
Buffer->ipi_freeopts = IPFreeOptions;
|
|
Buffer->ipi_qinfo = IPQueryInfo;
|
|
Buffer->ipi_setinfo = IPSetInfo;
|
|
Buffer->ipi_getelist = IPGetEList;
|
|
Buffer->ipi_setmcastaddr = IPSetMCastAddr;
|
|
Buffer->ipi_invalidsrc = InvalidSourceAddress;
|
|
Buffer->ipi_isdhcpinterface = IsDHCPInterface;
|
|
|
|
return IP_SUCCESS;
|
|
|
|
}
|
|
|
|
//** IPTimeout - IP timeout handler.
|
|
//
|
|
// The timeout routine called periodically to time out various things, such as entries
|
|
// being reassembled and ICMP echo requests.
|
|
//
|
|
// Entry: Timer - Timer being fired.
|
|
// Context - Pointer to NTE being time out.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
IPTimeout(CTEEvent *Timer, void *Context)
|
|
{
|
|
NetTableEntry *NTE = STRUCT_OF(NetTableEntry, Timer, nte_timer);
|
|
CTELockHandle NTEHandle;
|
|
ReassemblyHeader *PrevRH, *CurrentRH, *TempList = (ReassemblyHeader *)NULL;
|
|
|
|
ICMPTimer(NTE);
|
|
IGMPTimer(NTE);
|
|
if (Context) {
|
|
CTEGetLock(&NTE->nte_lock, &NTEHandle);
|
|
PrevRH = STRUCT_OF(ReassemblyHeader, &NTE->nte_ralist, rh_next);
|
|
CurrentRH = PrevRH->rh_next;
|
|
while (CurrentRH) {
|
|
if (--CurrentRH->rh_ttl == 0) { // This guy timed out.
|
|
PrevRH->rh_next = CurrentRH->rh_next; // Take him out.
|
|
CurrentRH->rh_next = TempList; // And save him for later.
|
|
TempList = CurrentRH;
|
|
IPSInfo.ipsi_reasmfails++;
|
|
} else
|
|
PrevRH = CurrentRH;
|
|
|
|
CurrentRH = PrevRH->rh_next;
|
|
}
|
|
|
|
// We've run the list. If we need to free anything, do it now. This may
|
|
// include sending an ICMP message.
|
|
CTEFreeLock(&NTE->nte_lock, NTEHandle);
|
|
while (TempList) {
|
|
CurrentRH = TempList;
|
|
TempList = CurrentRH->rh_next;
|
|
// If this wasn't sent to a bcast address and we already have the first fragment,
|
|
// send a time exceeded message.
|
|
if (CurrentRH->rh_headersize != 0)
|
|
SendICMPErr(NTE->nte_addr, (IPHeader *)CurrentRH->rh_header, ICMP_TIME_EXCEED,
|
|
TTL_IN_REASSEM, 0);
|
|
FreeRH(CurrentRH);
|
|
}
|
|
|
|
CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, NULL);
|
|
} else
|
|
CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, NTE);
|
|
|
|
}
|
|
|
|
//* IPpSetNTEAddr - Set the IP address of an NTE.
|
|
//
|
|
// Called by the DHCP client to set or delete the IP address of an NTE. We
|
|
// make sure he's specifiying a valid NTE, then mark it up or down as needed,
|
|
// notify the upper layers of the change if necessary, and then muck with
|
|
// the routing tables.
|
|
//
|
|
// Input: Context - Context of NTE to alter.
|
|
// Addr - IP address to set.
|
|
// Mask - Subnet mask for Addr.
|
|
//
|
|
// Returns: TRUE if we changed the address, FALSE otherwise.
|
|
//
|
|
IP_STATUS
|
|
IPpSetNTEAddr(NetTableEntry *NTE, IPAddr Addr, IPMask Mask,
|
|
CTELockHandle *RouteTableHandle, SetAddrControl *ControlBlock, SetAddrRtn Rtn)
|
|
{
|
|
Interface *IF;
|
|
uint (*CallFunc)(struct RouteTableEntry *, void *, void *);
|
|
|
|
IF = NTE->nte_if;
|
|
DHCPActivityCount++;
|
|
|
|
if (IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) {
|
|
// We're deleting an address.
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
// The address is currently valid. Fix that.
|
|
|
|
NTE->nte_flags &= ~NTE_VALID;
|
|
|
|
//
|
|
// If the old address is in the ATCache, flush it out.
|
|
//
|
|
FlushATCache(NTE->nte_addr);
|
|
|
|
if (--(IF->if_ntecount) == 0) {
|
|
// This is the last one, so we'll need to delete relevant
|
|
// routes.
|
|
CallFunc = DeleteRTEOnIF;
|
|
} else
|
|
CallFunc = InvalidateRCEOnIF;
|
|
|
|
CTEFreeLock(&RouteTableLock, *RouteTableHandle);
|
|
|
|
StopIGMPForNTE(NTE);
|
|
|
|
// Now call the upper layers, and tell them that address is
|
|
// gone. We really need to do something about locking here.
|
|
#ifdef _PNP_POWER
|
|
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
|
|
NTE->nte_context, &NTE->nte_addrhandle, NULL, FALSE);
|
|
|
|
#else // _PNP_POWER
|
|
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL,
|
|
NTE->nte_context, NULL, NULL, FALSE);
|
|
|
|
#endif // _PNP_POWER
|
|
|
|
// Call RTWalk to take the appropriate action on the RTEs.
|
|
RTWalk(CallFunc, IF, NULL);
|
|
|
|
// Delete the route to the address itself.
|
|
DeleteRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL,
|
|
LoopNTE->nte_if);
|
|
|
|
// Tell the lower interface this address is gone.
|
|
(*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_LOCAL, NTE->nte_addr,
|
|
NULL_IP_ADDR);
|
|
|
|
CTEGetLock(&RouteTableLock, RouteTableHandle);
|
|
}
|
|
|
|
DHCPActivityCount--;
|
|
CTEFreeLock(&RouteTableLock, *RouteTableHandle);
|
|
return IP_SUCCESS;
|
|
} else {
|
|
uint Status;
|
|
|
|
// We're not deleting, we're setting the address.
|
|
if (!(NTE->nte_flags & NTE_VALID)) {
|
|
uint index;
|
|
|
|
// The address is invalid. Save the info, mark him as valid,
|
|
// and add the routes.
|
|
NTE->nte_addr = Addr;
|
|
NTE->nte_mask = Mask;
|
|
NTE->nte_flags |= NTE_VALID;
|
|
IF->if_ntecount++;
|
|
index = IF->if_index;
|
|
|
|
//
|
|
// If the new address is in the ATCache, flush it out, otherwise
|
|
// TdiOpenAddress may fail.
|
|
//
|
|
FlushATCache(Addr);
|
|
|
|
CTEFreeLock(&RouteTableLock, *RouteTableHandle);
|
|
|
|
if (AddNTERoutes(NTE))
|
|
Status = TRUE;
|
|
else
|
|
Status = FALSE;
|
|
|
|
// Need to tell the lower layer about it.
|
|
if (Status) {
|
|
Interface *IF = NTE->nte_if;
|
|
|
|
ControlBlock->sac_rtn = Rtn;
|
|
Status = (*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_LOCAL,
|
|
Addr, Mask, ControlBlock );
|
|
}
|
|
|
|
if (Status == FALSE) {
|
|
// Couldn't add the routes. Recurively mark this NTE as down.
|
|
IPSetNTEAddr(NTE->nte_context, NULL_IP_ADDR, 0, NULL, NULL);
|
|
} else {
|
|
InitIGMPForNTE(NTE);
|
|
|
|
// Now call the upper layers, and tell them that address is
|
|
// is here. We really need to do something about locking here.
|
|
#ifdef _PNP_POWER
|
|
|
|
#ifdef SECFLTR
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask,
|
|
NTE->nte_pnpcontext, NTE->nte_context, &NTE->nte_addrhandle,
|
|
&(IF->if_configname), TRUE);
|
|
|
|
#else // SECFLTR
|
|
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask,
|
|
NTE->nte_pnpcontext, NTE->nte_context, &NTE->nte_addrhandle,
|
|
NULL, TRUE);
|
|
#endif // SECFLTR
|
|
|
|
#else // _PNP_POWER
|
|
|
|
#ifdef SECFLTR
|
|
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL,
|
|
NTE->nte_context, NULL, &(IF->if_configname), TRUE);
|
|
|
|
#else // SECFLTR
|
|
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL,
|
|
NTE->nte_context, NULL, NULL, TRUE);
|
|
|
|
#endif // SECFLTR
|
|
#endif // _PNP_POWER
|
|
|
|
#ifdef NT
|
|
if (!IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) {
|
|
SetPersistentRoutesForNTE(
|
|
net_long(Addr),
|
|
net_long(Mask),
|
|
index
|
|
);
|
|
}
|
|
#endif // NT
|
|
|
|
if ( (Status != IP_PENDING) && (Rtn != NULL) ) {
|
|
(*Rtn)(ControlBlock, IP_SUCCESS);
|
|
}
|
|
}
|
|
|
|
CTEGetLock(&RouteTableLock, RouteTableHandle);
|
|
NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY;
|
|
NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING;
|
|
} else
|
|
Status = FALSE;
|
|
|
|
DHCPActivityCount--;
|
|
CTEFreeLock(&RouteTableLock, *RouteTableHandle);
|
|
if (Status) {
|
|
return IP_PENDING;
|
|
} else {
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//* IPSetNTEAddr - Set the IP address of an NTE.
|
|
//
|
|
// Wrapper routine for IPpSetNTEAddr
|
|
//
|
|
// Input: Context - Context of NTE to alter.
|
|
// Addr - IP address to set.
|
|
// Mask - Subnet mask for Addr.
|
|
//
|
|
// Returns: TRUE if we changed the address, FALSE otherwise.
|
|
//
|
|
uint
|
|
IPSetNTEAddr(ushort Context, IPAddr Addr, IPMask Mask, SetAddrControl *ControlBlock, SetAddrRtn Rtn)
|
|
{
|
|
CTELockHandle Handle;
|
|
uint Status;
|
|
NetTableEntry *NTE;
|
|
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
|
|
if (NTE->nte_context == Context)
|
|
break;
|
|
|
|
if (NTE == NULL || NTE == LoopNTE) {
|
|
// Can't alter the loopback NTE, or one we didn't find.
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
|
|
Status = IPpSetNTEAddr(NTE, Addr, Mask, &Handle, ControlBlock, Rtn);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
#pragma BEGIN_INIT
|
|
|
|
extern NetTableEntry *InitLoopback(IPConfigInfo *);
|
|
|
|
//** InitTimestamp - Intialize the timestamp for outgoing packets.
|
|
//
|
|
// Called at initialization time to setup our first timestamp. The timestamp we use
|
|
// is the in ms since midnite GMT at which the system started.
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
InitTimestamp()
|
|
{
|
|
ulong GMTDelta; // Delta in ms from GMT.
|
|
ulong Now; // Milliseconds since midnight.
|
|
|
|
TimeStamp = 0;
|
|
|
|
if ((GMTDelta = GetGMTDelta()) == 0xffffffff) { // Had some sort of error.
|
|
TSFlag = 0x80000000;
|
|
return;
|
|
}
|
|
|
|
if ((Now = GetTime()) > (24L*3600L*1000L)) { // Couldn't get time since midnight.
|
|
TSFlag = net_long(0x80000000);
|
|
return;
|
|
}
|
|
|
|
TimeStamp = Now + GMTDelta - CTESystemUpTime();
|
|
TSFlag = 0;
|
|
|
|
}
|
|
#pragma END_INIT
|
|
|
|
#ifndef CHICAGO
|
|
#pragma BEGIN_INIT
|
|
#else
|
|
#pragma code_seg("_LTEXT", "LCODE")
|
|
#endif
|
|
|
|
//** InitNTE - Initialize an NTE.
|
|
//
|
|
// This routine is called during initialization to initialize an NTE. We
|
|
// allocate memory, NDIS resources, etc.
|
|
//
|
|
//
|
|
// Entry: NTE - Pointer to NTE to be initalized.
|
|
//
|
|
// Returns: 0 if initialization failed, non-zero if it succeeds.
|
|
//
|
|
int
|
|
InitNTE(NetTableEntry *NTE)
|
|
{
|
|
Interface *IF;
|
|
NetTableEntry *PrevNTE;
|
|
|
|
NTE->nte_ralist = NULL;
|
|
NTE->nte_echolist = NULL;
|
|
|
|
//
|
|
// Taken together, the context and instance numbers uniquely identify
|
|
// a network entry, even across boots of the system. The instance number
|
|
// will have to become dynamic if contexts are ever reused.
|
|
//
|
|
NTE->nte_context = NextNTEContext++;
|
|
NTE->nte_rtrlist = NULL;
|
|
NTE->nte_instance = GetUnique32BitValue();
|
|
|
|
// Now link him on the IF chain, and bump the count.
|
|
IF = NTE->nte_if;
|
|
PrevNTE = STRUCT_OF(NetTableEntry, &IF->if_nte, nte_ifnext);
|
|
while (PrevNTE->nte_ifnext != NULL)
|
|
PrevNTE = PrevNTE->nte_ifnext;
|
|
|
|
PrevNTE->nte_ifnext = NTE;
|
|
NTE->nte_ifnext = NULL;
|
|
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
IF->if_ntecount++;
|
|
}
|
|
|
|
CTEInitTimer(&NTE->nte_timer);
|
|
CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, (void *)NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
//** InitInterface - Initialize with an interface.
|
|
//
|
|
// Called when we need to initialize with an interface. We set the appropriate NTE
|
|
// info, then register our local address and any appropriate broadcast addresses
|
|
// with the interface. We assume the NTE being initialized already has an interface
|
|
// pointer set up for it. We also allocate at least one TD buffer for use on the interface.
|
|
//
|
|
// Input: NTE - NTE to initialize with the interface.
|
|
//
|
|
// Returns: TRUE is we succeeded, FALSE if we fail.
|
|
//
|
|
int
|
|
InitInterface(NetTableEntry *NTE)
|
|
{
|
|
IPMask netmask = IPNetMask(NTE->nte_addr);
|
|
uchar *TDBuffer; // Pointer to tdbuffer
|
|
PNDIS_PACKET Packet;
|
|
NDIS_HANDLE TDbpool; // Handle for TD buffer pool.
|
|
NDIS_HANDLE TDppool;
|
|
PNDIS_BUFFER TDBufDesc; // Buffer descriptor for TDBuffer.
|
|
NDIS_STATUS Status;
|
|
Interface *IF; // Interface for this NTE.
|
|
CTELockHandle Handle;
|
|
|
|
|
|
IF = NTE->nte_if;
|
|
|
|
CTEAssert(NTE->nte_mss > sizeof(IPHeader));
|
|
CTEAssert(IF->if_mtu > 0);
|
|
|
|
NTE->nte_mss = MIN((NTE->nte_mss - sizeof(IPHeader)), IF->if_mtu);
|
|
|
|
CTERefillMem();
|
|
|
|
// Allocate resources needed for xfer data calls. The TD buffer has to be as large
|
|
// as any frame that can be received, even though our MSS may be smaller, because we
|
|
// can't control what might be sent at us.
|
|
TDBuffer = CTEAllocMem(IF->if_mtu);
|
|
if (TDBuffer == (uchar *)NULL)
|
|
return FALSE;
|
|
|
|
NdisAllocatePacketPool(&Status, &TDppool, 1, sizeof(TDContext));
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
CTEFreeMem(TDBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
NdisAllocatePacket(&Status, &Packet, TDppool);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
NdisFreePacketPool(TDppool);
|
|
CTEFreeMem(TDBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
CTEMemSet(Packet->ProtocolReserved, 0, sizeof(TDContext));
|
|
|
|
NdisAllocateBufferPool(&Status, &TDbpool, 1);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
NdisFreePacketPool(TDppool);
|
|
CTEFreeMem(TDBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
NdisAllocateBuffer(&Status,&TDBufDesc, TDbpool, TDBuffer,
|
|
(IF->if_mtu + sizeof(IPHeader)));
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
NdisFreeBufferPool(TDbpool);
|
|
NdisFreePacketPool(TDppool);
|
|
CTEFreeMem(TDBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
NdisChainBufferAtFront(Packet, TDBufDesc);
|
|
|
|
((TDContext *)Packet->ProtocolReserved)->tdc_buffer = TDBuffer;
|
|
|
|
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
|
|
// Add our local IP address.
|
|
if (!(*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_LOCAL,
|
|
NTE->nte_addr, NTE->nte_mask, NULL)) {
|
|
NdisFreeBufferPool(TDbpool);
|
|
NdisFreePacketPool(TDppool);
|
|
CTEFreeMem(TDBuffer);
|
|
return FALSE; // Couldn't add local address.
|
|
}
|
|
}
|
|
|
|
// Set up the broadcast addresses for this interface, iff we're the
|
|
// 'primary' NTE on the interface.
|
|
if (NTE->nte_flags & NTE_PRIMARY) {
|
|
|
|
if (!(*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_BCAST,
|
|
NTE->nte_if->if_bcast, 0, NULL)) {
|
|
NdisFreeBufferPool(TDbpool);
|
|
NdisFreePacketPool(TDppool);
|
|
CTEFreeMem(TDBuffer);
|
|
return FALSE; // Couldn't add broadcast address.
|
|
}
|
|
}
|
|
|
|
if (IF->if_llipflags & LIP_COPY_FLAG) {
|
|
NTE->nte_flags |= NTE_COPY;
|
|
}
|
|
|
|
CTEGetLock(&IF->if_lock, &Handle);
|
|
((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link = IF->if_tdpacket;
|
|
IF->if_tdpacket = Packet;
|
|
CTEFreeLock(&IF->if_lock, Handle);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifndef _PNP_POWER
|
|
//* CleanAdaptTable - Clean up the adapter name table.
|
|
//
|
|
//
|
|
void
|
|
CleanAdaptTable()
|
|
{
|
|
int i = 0;
|
|
|
|
while (AdptNameTable[i].nm_arpinfo != NULL) {
|
|
CTEFreeMem(AdptNameTable[i].nm_arpinfo);
|
|
CTEFreeString(&AdptNameTable[i].nm_name);
|
|
if (AdptNameTable[i].nm_driver.Buffer != NULL)
|
|
CTEFreeString(&AdptNameTable[i].nm_driver);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
|
|
//* OpenAdapters - Clean up the adapter name table.
|
|
//
|
|
// Used at the end of initialization. We loop through and 'open' all the adapters.
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
OpenAdapters()
|
|
{
|
|
int i = 0;
|
|
LLIPBindInfo *ABI;
|
|
|
|
while ((ABI = AdptNameTable[i++].nm_arpinfo) != NULL) {
|
|
(*(ABI->lip_open))(ABI->lip_context);
|
|
}
|
|
}
|
|
|
|
|
|
//* IPRegisterDriver - Called during init time to register a driver.
|
|
//
|
|
// Called during init time when we have a non-LAN (or non-ARPable) driver
|
|
// that wants to register with us. We try to find a free slot in the table
|
|
// to register him.
|
|
//
|
|
// Input: Name - Pointer to the name of the driver to be registered.
|
|
// Ptr - Pointer to driver's registration function.
|
|
//
|
|
// Returns: TRUE if we succeeded, FALSE if we fail.
|
|
//
|
|
uint
|
|
IPRegisterDriver(PNDIS_STRING Name, LLIPRegRtn Ptr)
|
|
{
|
|
uint i;
|
|
|
|
CTERefillMem();
|
|
|
|
// First, find a slot for him.
|
|
for (i = 0; i < MaxIPNets; i++) {
|
|
if (DriverNameTable[i].drm_driver.Buffer == NULL) {
|
|
// Found a slot. Try and allocate and copy a string for him.
|
|
if (!CTEAllocateString(&DriverNameTable[i].drm_driver,
|
|
CTELengthString(Name)))
|
|
return FALSE;
|
|
// Got the space. Copy the string and the pointer.
|
|
CTECopyString(&DriverNameTable[i].drm_driver, Name);
|
|
DriverNameTable[i].drm_regptr = Ptr;
|
|
NumRegDrivers++;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#ifdef NT
|
|
|
|
//* GetLLRegPtr - Called during init time to get a lower driver's registration
|
|
// routine.
|
|
//
|
|
// Called during init time to locate the registration function of a
|
|
// non-LAN (or non-ARPable) driver.
|
|
//
|
|
// Input: Name - Pointer to the name of the driver to be registered.
|
|
//
|
|
// Returns: A pointer to the driver's registration routine or NULL on failure.
|
|
//
|
|
LLIPRegRtn
|
|
GetLLRegPtr(PNDIS_STRING Name)
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
LLIPIF_REGISTRATION_DATA registrationData;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
PIRP irp;
|
|
KEVENT ioctlEvent;
|
|
extern POBJECT_TYPE *IoDeviceObjectType;
|
|
|
|
|
|
registrationData.RegistrationFunction = NULL;
|
|
|
|
KeInitializeEvent(&ioctlEvent, SynchronizationEvent, FALSE);
|
|
|
|
status = IoGetDeviceObjectPointer(
|
|
Name,
|
|
SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
|
|
&fileObject,
|
|
&deviceObject
|
|
);
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
CTEPrint("IP failed to open the lower layer driver\n");
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Reference the device object.
|
|
//
|
|
ObReferenceObject(deviceObject);
|
|
|
|
//
|
|
// IoGetDeviceObjectPointer put a reference on the file object.
|
|
//
|
|
ObDereferenceObject(fileObject);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_LLIPIF_REGISTER,
|
|
deviceObject,
|
|
NULL, // input Buffer
|
|
0, // input buffer length
|
|
®istrationData,
|
|
sizeof(LLIPIF_REGISTRATION_DATA),
|
|
FALSE, // not an InternalDeviceControl
|
|
&ioctlEvent,
|
|
&ioStatusBlock
|
|
);
|
|
|
|
if (irp == NULL) {
|
|
ObDereferenceObject(deviceObject);
|
|
return(NULL);
|
|
}
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
status = KeWaitForSingleObject(
|
|
&ioctlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL // no timeout
|
|
);
|
|
|
|
}
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
return(NULL);
|
|
}
|
|
|
|
if (registrationData.RegistrationFunction != NULL) {
|
|
//
|
|
// Cache the driver registration for future reference.
|
|
//
|
|
IPRegisterDriver(Name, registrationData.RegistrationFunction);
|
|
}
|
|
|
|
return(registrationData.RegistrationFunction);
|
|
|
|
} // GetLLRegPtr
|
|
|
|
#endif // NT
|
|
#endif // _PNP_POWER
|
|
|
|
|
|
#ifndef _PNP_POWER
|
|
|
|
//* FindRegPtr - Find a driver's registration routine.
|
|
//
|
|
// Called during init time when we have a non-LAN (or non-ARPable) driver to
|
|
// register with. We take in the driver name, and try to find a registration
|
|
// pointer for the driver.
|
|
//
|
|
// Input: Name - Pointer to the name of the driver to be found.
|
|
//
|
|
// Returns: Pointer to the registration routine, or NULL if there is none.
|
|
//
|
|
LLIPRegRtn
|
|
FindRegPtr(PNDIS_STRING Name)
|
|
{
|
|
uint i;
|
|
|
|
for (i = 0; i < NumRegDrivers; i++) {
|
|
if (CTEEqualString(&(DriverNameTable[i].drm_driver), Name))
|
|
return (LLIPRegRtn)(DriverNameTable[i].drm_regptr);
|
|
}
|
|
|
|
#ifdef NT
|
|
//
|
|
// For NT, we open the lower driver and issue an IOCTL to get a pointer to
|
|
// its registration function. We then cache this in the table for future
|
|
// reference.
|
|
//
|
|
return(GetLLRegPtr(Name));
|
|
#else
|
|
return NULL;
|
|
#endif // NT
|
|
}
|
|
|
|
#endif // _PNP_POWER
|
|
|
|
#ifdef CHICAGO
|
|
#pragma BEGIN_INIT
|
|
#endif
|
|
|
|
//* FreeNets - Free nets we have allocated.
|
|
//
|
|
// Called during init time if initialization fails. We walk down our list
|
|
// of nets, and free them.
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
FreeNets(void)
|
|
{
|
|
NetTableEntry *NTE;
|
|
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next)
|
|
CTEFreeMem(NTE);
|
|
}
|
|
|
|
#ifdef CHICAGO
|
|
#pragma END_INIT
|
|
#pragma code_seg("_LTEXT", "LCODE")
|
|
#endif
|
|
|
|
|
|
#ifdef _PNP_POWER
|
|
|
|
extern uint GetGeneralIFConfig(IFGeneralConfig *GConfigInfo, NDIS_HANDLE Handle);
|
|
extern IFAddrList *GetIFAddrList(uint *NumAddr, NDIS_HANDLE Handle);
|
|
|
|
|
|
#ifdef CHICAGO
|
|
|
|
extern void RequestDHCPAddr(ushort context);
|
|
|
|
#define MAX_NOTIFY_CLIENTS 8
|
|
|
|
typedef void (*AddrNotifyRtn)(IPAddr Addr, IPMask Mask, void *Context,
|
|
ushort IPContext, uint Added);
|
|
|
|
AddrNotifyRtn AddrNotifyTable[MAX_NOTIFY_CLIENTS];
|
|
|
|
typedef void (*InterfaceNotifyRtn)(ushort Context, uint Added);
|
|
|
|
InterfaceNotifyRtn InterfaceNotifyTable[MAX_NOTIFY_CLIENTS];
|
|
|
|
//* RegisterAddrNotify - Register an address notify routine.
|
|
//
|
|
// A routine called to register an address notify routine.
|
|
//
|
|
// Input: Rtn - Routine to register.
|
|
// Register - True to register, False to deregister.
|
|
//
|
|
// Returns: TRUE if we succeed, FALSE if we don't/
|
|
//
|
|
uint
|
|
RegisterAddrNotify(AddrNotifyRtn Rtn, uint Register)
|
|
{
|
|
uint i;
|
|
AddrNotifyRtn NewRtn, OldRtn;
|
|
|
|
if (Register) {
|
|
NewRtn = Rtn;
|
|
OldRtn = NULL;
|
|
} else {
|
|
NewRtn = NULL;
|
|
OldRtn = Rtn;
|
|
}
|
|
|
|
for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) {
|
|
if (AddrNotifyTable[i] == OldRtn) {
|
|
AddrNotifyTable[i] = NewRtn;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//* NotifyInterfaceChange - Notify clients of a change in an interface.
|
|
//
|
|
// Called when we want to notify registered clients that an interface has come
|
|
// or gone. We loop through our InterfaceNotify table, calling each one.
|
|
//
|
|
// Input: Context - Context for interface that has changed.
|
|
// Added - True if the interface is coming, False if it's
|
|
// going.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
NotifyInterfaceChange(ushort IPContext, uint Added)
|
|
{
|
|
uint i;
|
|
|
|
for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) {
|
|
if (InterfaceNotifyTable[i] != NULL)
|
|
(*(InterfaceNotifyTable[i]))(IPContext, Added);
|
|
}
|
|
}
|
|
|
|
//* RegisterInterfaceNotify - Register an interface notify routine.
|
|
//
|
|
// A routine called to register an interface notify routine.
|
|
//
|
|
// Input: Rtn - Routine to register.
|
|
// Register - True to register, False to deregister.
|
|
//
|
|
// Returns: TRUE if we succeed, FALSE if we don't/
|
|
//
|
|
uint
|
|
RegisterInterfaceNotify(InterfaceNotifyRtn Rtn, uint Register)
|
|
{
|
|
uint i;
|
|
InterfaceNotifyRtn NewRtn, OldRtn;
|
|
|
|
if (Register) {
|
|
NewRtn = Rtn;
|
|
OldRtn = NULL;
|
|
} else {
|
|
NewRtn = NULL;
|
|
OldRtn = Rtn;
|
|
}
|
|
|
|
for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) {
|
|
if (InterfaceNotifyTable[i] == OldRtn) {
|
|
InterfaceNotifyTable[i] = NewRtn;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//* NotifyAddrChange - Notify clients of a change in addresses.
|
|
//
|
|
// Called when we want to notify registered clients that an address has come
|
|
// or gone. We loop through our AddrNotify table, calling each one.
|
|
//
|
|
// Input: Addr - Addr that has changed.
|
|
// Mask - Mask that has changed.
|
|
// Context - PNP context for address
|
|
// IPContext - NTE context for NTE
|
|
// Handle - Pointer to where to get/set address registration
|
|
// handle
|
|
// ConfigName - Registry name to use to retrieve config info.
|
|
// Added - True if the addr is coming, False if it's going.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext,
|
|
PVOID *Handle, PNDIS_STRING ConfigName, uint Added)
|
|
{
|
|
uint i;
|
|
|
|
for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) {
|
|
if (AddrNotifyTable[i] != NULL)
|
|
(*(AddrNotifyTable[i]))(Addr, Mask, Context, IPContext, Added);
|
|
}
|
|
}
|
|
|
|
|
|
#else // CHICAGO
|
|
|
|
//* NotifyAddrChange - Notify clients of a change in addresses.
|
|
//
|
|
// Called when we want to notify registered clients that an address has come
|
|
// or gone. We call TDI to perform this function.
|
|
//
|
|
// Input: Addr - Addr that has changed.
|
|
// Mask - Mask that has changed.
|
|
// Context - PNP context for address
|
|
// IPContext - NTE context for NTE
|
|
// Handle - Pointer to where to get/set address registration
|
|
// handle
|
|
// ConfigName - Registry name to use to retrieve config info.
|
|
// Added - True if the addr is coming, False if it's going.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext,
|
|
PVOID *Handle, PNDIS_STRING ConfigName, uint Added)
|
|
{
|
|
uchar Address[sizeof(TA_ADDRESS) + sizeof(TDI_ADDRESS_IP)];
|
|
PTA_ADDRESS AddressPtr;
|
|
PTDI_ADDRESS_IP IPAddressPtr;
|
|
NTSTATUS Status;
|
|
|
|
#ifdef SECFLTR
|
|
|
|
IP_STATUS StatusType;
|
|
NDIS_HANDLE ConfigHandle = NULL;
|
|
int i;
|
|
ULStatusProc StatProc;
|
|
|
|
#endif // SECFLTR
|
|
|
|
|
|
AddressPtr = (PTA_ADDRESS)Address;
|
|
|
|
AddressPtr->AddressLength = sizeof(TDI_ADDRESS_IP);
|
|
AddressPtr->AddressType = TDI_ADDRESS_TYPE_IP;
|
|
|
|
IPAddressPtr = (PTDI_ADDRESS_IP)AddressPtr->Address;
|
|
|
|
CTEMemSet(IPAddressPtr, 0, sizeof(TDI_ADDRESS_IP));
|
|
|
|
IPAddressPtr->in_addr = Addr;
|
|
|
|
#ifdef SECFLTR
|
|
|
|
//
|
|
// Call the status entrypoint of the transports so they can
|
|
// adjust their security filters.
|
|
//
|
|
if (Added) {
|
|
StatusType = IP_ADDR_ADDED;
|
|
|
|
//
|
|
// Open a configuration key
|
|
//
|
|
if (!OpenIFConfig(ConfigName, &ConfigHandle)) {
|
|
//
|
|
// Not much we can do. The transports will have
|
|
// to handle this.
|
|
//
|
|
CTEAssert(ConfigHandle == NULL);
|
|
}
|
|
}
|
|
else {
|
|
StatusType = IP_ADDR_DELETED;
|
|
}
|
|
|
|
for ( i = 0; i < NextPI; i++) {
|
|
StatProc = IPProtInfo[i].pi_status;
|
|
if (StatProc != NULL)
|
|
(*StatProc)(IP_HW_STATUS, StatusType, Addr, NULL_IP_ADDR,
|
|
NULL_IP_ADDR, 0, ConfigHandle );
|
|
}
|
|
|
|
if (ConfigHandle != NULL) {
|
|
CloseIFConfig(ConfigHandle);
|
|
}
|
|
|
|
#endif // SECFLTR
|
|
|
|
//
|
|
// Notify any interested parties via TDI. The transports all register
|
|
// for this notification as well.
|
|
//
|
|
if (Added) {
|
|
Status = TdiRegisterNetAddress(AddressPtr, Handle);
|
|
if (Status != STATUS_SUCCESS) {
|
|
*Handle = NULL;
|
|
}
|
|
} else {
|
|
if (*Handle != NULL) {
|
|
TdiDeregisterNetAddress(*Handle);
|
|
*Handle = NULL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif // CHICAGO
|
|
|
|
|
|
//* IPAddNTE - Add a new NTE to an interface
|
|
//
|
|
// Called to create a new network entry on an interface.
|
|
//
|
|
// Input: GConfigInfo - Configuration information for the interface
|
|
// PNPContext - The PNP context value associated with the interface
|
|
// RegRtn - Routine to call to register with ARP.
|
|
// BindInfo - Pointer to NDIS bind information.
|
|
// IF - The interface on which to create the NTE.
|
|
// NewAddr - The address of the new NTE.
|
|
// NewMask - The subnet mask for the new NTE.
|
|
// IsPrimary - TRUE if this NTE is the primary one on the interface
|
|
// IsDynamic - TRUE if this NTE is being created on an
|
|
// existing interface instead of a new one.
|
|
//
|
|
// Returns: A pointer to the new NTE if the operation succeeds.
|
|
// NULL if the operation fails.
|
|
//
|
|
NetTableEntry *
|
|
IPAddNTE(IFGeneralConfig *GConfigInfo, void * PNPContext, LLIPRegRtn RegRtn,
|
|
LLIPBindInfo *BindInfo, Interface *IF, IPAddr NewAddr, IPMask NewMask,
|
|
uint IsPrimary, uint IsDynamic)
|
|
{
|
|
NetTableEntry *NTE, *PrevNTE;
|
|
CTELockHandle Handle;
|
|
|
|
|
|
// If the address is invalid we're done. Fail the request.
|
|
if (CLASSD_ADDR(NewAddr) || CLASSE_ADDR(NewAddr)) {
|
|
return NULL;
|
|
}
|
|
|
|
// See if we have an inactive NTE on the NetTableList. If we do, we'll
|
|
// just recycle that. We will pull him out of the list. This is not
|
|
// strictly MP safe, since other people could be walking the list while
|
|
// we're doing this without holding a lock, but it should be harmless.
|
|
// The removed NTE is marked as invalid, and his next pointer will
|
|
// be nulled, so anyone walking the list might hit the end too soon,
|
|
// but that's all. The memory is never freed, and the next pointer is
|
|
// never pointed at freed memory.
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
PrevNTE = STRUCT_OF(NetTableEntry, &NetTableList, nte_next);
|
|
for (NTE = NetTableList; NTE != NULL; PrevNTE = NTE, NTE = NTE->nte_next)
|
|
if (!(NTE->nte_flags & NTE_ACTIVE)) {
|
|
PrevNTE->nte_next = NTE->nte_next;
|
|
NTE->nte_next = NULL;
|
|
NumNTE--;
|
|
break;
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
|
|
// See if we got one.
|
|
if (NTE == NULL) {
|
|
// Didn't get one. Try to allocate one.
|
|
NTE = CTEAllocMem(sizeof(NetTableEntry));
|
|
if (NTE == NULL)
|
|
return NULL;
|
|
CTEMemSet(NTE, 0, sizeof(NetTableEntry));
|
|
}
|
|
|
|
// Initialize the address and mask stuff
|
|
NTE->nte_addr = NewAddr;
|
|
NTE->nte_mask = NewMask;
|
|
NTE->nte_mss = MAX(GConfigInfo->igc_mtu, 68);
|
|
NTE->nte_rtrdiscaddr = GConfigInfo->igc_rtrdiscaddr;
|
|
NTE->nte_rtrdiscstate = NTE_RTRDISC_UNINIT;
|
|
NTE->nte_rtrdisccount = 0;
|
|
NTE->nte_rtrdiscovery = (uchar)GConfigInfo->igc_rtrdiscovery;
|
|
NTE->nte_rtrlist = NULL;
|
|
NTE->nte_pnpcontext = PNPContext;
|
|
NTE->nte_if = IF;
|
|
NTE->nte_flags = NTE_ACTIVE;
|
|
|
|
//
|
|
// If the new address is in the ATCache, flush it out, otherwise
|
|
// TdiOpenAddress may fail.
|
|
//
|
|
FlushATCache(NewAddr);
|
|
|
|
if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
|
|
NTE->nte_flags |= NTE_VALID;
|
|
NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY;
|
|
NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING;
|
|
}
|
|
|
|
if (IsDynamic) {
|
|
NTE->nte_flags |= NTE_DYNAMIC;
|
|
}
|
|
|
|
NTE->nte_ralist = NULL;
|
|
NTE->nte_echolist = NULL;
|
|
NTE->nte_icmpseq = 0;
|
|
NTE->nte_igmplist = NULL;
|
|
CTEInitLock(&NTE->nte_lock);
|
|
CTEInitTimer(&NTE->nte_timer);
|
|
|
|
if (IsPrimary) {
|
|
//
|
|
// This is the first (primary) NTE on the interface.
|
|
//
|
|
NTE->nte_flags |= NTE_PRIMARY;
|
|
|
|
// Pass our information to the underlying code.
|
|
if (!(*RegRtn)(&(IF->if_configname), NTE, IPRcv, IPSendComplete,
|
|
IPStatus, IPTDComplete, IPRcvComplete, BindInfo,
|
|
IF->if_index)) {
|
|
|
|
// Couldn't register.
|
|
goto failure;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Link the NTE onto the global NTE list.
|
|
//
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
NTE->nte_next = NetTableList;
|
|
NetTableList = NTE;
|
|
NumNTE++;
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
|
|
if (!InitInterface(NTE)) {
|
|
goto failure;
|
|
}
|
|
|
|
if (!InitNTE(NTE)) {
|
|
goto failure;
|
|
}
|
|
|
|
if (!InitNTERouting(NTE, GConfigInfo->igc_numgws, GConfigInfo->igc_gw)) {
|
|
// Couldn't add the routes for this NTE. Mark him as not valid.
|
|
// Probably should log an event here.
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
NTE->nte_flags &= ~NTE_VALID;
|
|
NTE->nte_if->if_ntecount--;
|
|
}
|
|
}
|
|
|
|
#ifdef NT
|
|
|
|
if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
|
|
SetPersistentRoutesForNTE(
|
|
net_long(NTE->nte_addr),
|
|
net_long(NTE->nte_mask),
|
|
NTE->nte_if->if_index
|
|
);
|
|
}
|
|
|
|
#endif // NT
|
|
|
|
return(NTE);
|
|
|
|
failure:
|
|
|
|
//
|
|
// BUGBUG - what should we do with the NTE here????
|
|
//
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
//* IPAddDynamicNTE - Add a new "dynamic" NTE to an existing interface
|
|
//
|
|
// Called to dynamically create a new network entry on an existing interface.
|
|
// This entry was not configured when the interaface was originally created
|
|
// and will not persist if the interface is unbound.
|
|
//
|
|
// Input: InterfaceContext - The context value which identifies the
|
|
// interface on which to create the NTE.
|
|
// NewAddr - The address of the new NTE.
|
|
// NewMask - The subnet mask for the new NTE.
|
|
//
|
|
// Output: NTEContext - The context identifying the new NTE.
|
|
// NTEInstance - The instance number which (reasonably) uniquely
|
|
// identifies this NTE in time.
|
|
//
|
|
// Returns: Nonzero if the operation succeeded. Zero if it failed.
|
|
//
|
|
uint
|
|
IPAddDynamicNTE(ushort InterfaceContext, IPAddr NewAddr, IPMask NewMask,
|
|
ushort *NTEContext, ulong *NTEInstance)
|
|
{
|
|
IFGeneralConfig GConfigInfo; // General config info structure.
|
|
NDIS_HANDLE Handle; // Configuration handle.
|
|
NetTableEntry *NTE;
|
|
Interface *IF;
|
|
ushort MTU;
|
|
uint Flags = 0;
|
|
|
|
|
|
#ifdef NT
|
|
PAGED_CODE();
|
|
#endif
|
|
|
|
for (IF = IFList; IF != NULL; IF = IF->if_next) {
|
|
if (IF->if_index == InterfaceContext) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//* Try to get the network configuration information.
|
|
if (!OpenIFConfig(&(IF->if_configname), &Handle))
|
|
return FALSE;
|
|
|
|
// Try to get our general config information.
|
|
if (!GetGeneralIFConfig(&GConfigInfo, Handle)) {
|
|
goto failure;
|
|
}
|
|
|
|
NTE = IPAddNTE(
|
|
&GConfigInfo,
|
|
NULL, // PNPContext - BUGBUG needed?
|
|
NULL, // RegRtn - not needed if not primary
|
|
NULL, // BindInfo - not needed if not primary
|
|
IF,
|
|
NewAddr,
|
|
NewMask,
|
|
FALSE, // not primary
|
|
TRUE // is dynamic
|
|
);
|
|
|
|
if (NTE == NULL) {
|
|
goto failure;
|
|
}
|
|
|
|
CloseIFConfig(Handle);
|
|
|
|
//
|
|
// Notify upper layers of the new address.
|
|
//
|
|
#ifdef SECFLTR
|
|
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
|
|
NTE->nte_context, &NTE->nte_addrhandle, &(IF->if_configname), TRUE);
|
|
|
|
#else // SECFLTR
|
|
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
|
|
NTE->nte_context, &NTE->nte_addrhandle, NULL, TRUE);
|
|
|
|
#endif // SECFLTR
|
|
|
|
if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
|
|
InitIGMPForNTE(NTE);
|
|
}
|
|
else {
|
|
#ifdef CHICAGO
|
|
// Call DHCP to get an address for this guy.
|
|
|
|
//
|
|
// BUGBUG (mikemas 8/28/96)
|
|
// we may not always want to do this!
|
|
//
|
|
RequestDHCPAddr(NTE->nte_context);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Fill in the out parameter value.
|
|
//
|
|
*NTEContext = NTE->nte_context;
|
|
*NTEInstance = NTE->nte_instance;
|
|
|
|
return(TRUE);
|
|
|
|
failure:
|
|
|
|
CloseIFConfig(Handle);
|
|
|
|
return(IP_GENERAL_FAILURE);
|
|
}
|
|
|
|
|
|
//* IPAddInterface - Add an interface.
|
|
//
|
|
// Called when someone has an interface they want us to add. We read our
|
|
// configuration information, and see if we have it listed. If we do,
|
|
// we'll try to allocate memory for the structures we need. Then we'll
|
|
// call back to the guy who called us to get things going. Finally, we'll
|
|
// see if we have an address that needs to be DHCP'ed.
|
|
//
|
|
// Input: ConfigName - Name of config info we're to read.
|
|
// Context - Context to pass to i/f on calls.
|
|
// RegRtn - Routine to call to register.
|
|
// BindInfo - Pointer to bind information.
|
|
//
|
|
// Returns: Status of attempt to add the interface.
|
|
//
|
|
IP_STATUS
|
|
IPAddInterface(PNDIS_STRING ConfigName, void *PNPContext, void *Context,
|
|
LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo)
|
|
{
|
|
IFGeneralConfig GConfigInfo; // General config info structure.
|
|
IFAddrList *AddrList; // List of addresses for this I/F.
|
|
uint NumAddr; // Number of IP addresses on this
|
|
// interface
|
|
NetTableEntry *NTE; // Current NTE being initialized.
|
|
uint i; // Index variable.
|
|
uint IndexMask; // Mask for searching IFBitMask.
|
|
Interface *IF; // Interface being added.
|
|
NDIS_HANDLE Handle; // Configuration handle.
|
|
NetTableEntry *PrimaryNTE; // The primary NTE for this I/F.
|
|
uint IFIndex; // Index to be assigned to this I/F.
|
|
NetTableEntry *LastNTE; // Last NTE created.
|
|
|
|
|
|
CTERefillMem();
|
|
|
|
PrimaryNTE = NULL;
|
|
AddrList = NULL;
|
|
IF = NULL;
|
|
LastNTE = NULL;
|
|
|
|
//* First, try to get the network configuration information.
|
|
if (!OpenIFConfig(ConfigName, &Handle))
|
|
return IP_GENERAL_FAILURE; // Couldn't get IFConfig.
|
|
|
|
// Try to get our general config information.
|
|
if (!GetGeneralIFConfig(&GConfigInfo, Handle)) {
|
|
goto failure;
|
|
}
|
|
|
|
// We got the general config info. Now allocate an interface.
|
|
IF = CTEAllocMem(InterfaceSize + ConfigName->MaximumLength);
|
|
|
|
if (IF == NULL) {
|
|
goto failure;
|
|
}
|
|
|
|
CTEMemSet(IF, 0, InterfaceSize);
|
|
CTEInitLock(&IF->if_lock);
|
|
|
|
// Initialize the broadcast we'll use.
|
|
if (GConfigInfo.igc_zerobcast)
|
|
IF->if_bcast = IP_ZERO_BCST;
|
|
else
|
|
IF->if_bcast = IP_LOCAL_BCST;
|
|
|
|
if (RouterConfigured) {
|
|
RouteInterface *RtIF = (RouteInterface *)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_maxpending = GConfigInfo.igc_maxpending;
|
|
RtIF->ri_q.rsq_qlength = 0;
|
|
CTEInitLock(&RtIF->ri_q.rsq_lock);
|
|
}
|
|
|
|
IF->if_xmit = BindInfo->lip_transmit;
|
|
IF->if_transfer = BindInfo->lip_transfer;
|
|
IF->if_close = BindInfo->lip_close;
|
|
IF->if_invalidate = BindInfo->lip_invalidate;
|
|
IF->if_lcontext = BindInfo->lip_context;
|
|
IF->if_addaddr = BindInfo->lip_addaddr;
|
|
IF->if_deladdr = BindInfo->lip_deladdr;
|
|
IF->if_qinfo = BindInfo->lip_qinfo;
|
|
IF->if_setinfo = BindInfo->lip_setinfo;
|
|
IF->if_getelist = BindInfo->lip_getelist;
|
|
IF->if_tdpacket = NULL;
|
|
CTEAssert(BindInfo->lip_mss > sizeof(IPHeader));
|
|
IF->if_mtu = BindInfo->lip_mss - sizeof(IPHeader);
|
|
IF->if_speed = BindInfo->lip_speed;
|
|
IF->if_flags = BindInfo->lip_flags & LIP_P2P_FLAG ? IF_FLAGS_P2P : 0;
|
|
IF->if_addrlen = BindInfo->lip_addrlen;
|
|
IF->if_addr = BindInfo->lip_addr;
|
|
IF->if_pnpcontext = PNPContext;
|
|
IF->if_llipflags = BindInfo->lip_flags;
|
|
|
|
// Initialize the reference count to 1, for the open.
|
|
IF->if_refcount = 1;
|
|
|
|
#ifdef IGMPV2
|
|
IF->IgmpVersion = IGMPV2;
|
|
#else
|
|
IF->IgmpVersion = IGMPV1;
|
|
#endif
|
|
|
|
|
|
//
|
|
// No need to do the following since IF structure is inited to 0 through
|
|
// memset above
|
|
//
|
|
// IF->IgmpVer1Timeout = 0;
|
|
|
|
//
|
|
// Copy the config string for use later when DHCP enables an address
|
|
// on this interface or when an NTE is added dynamically.
|
|
//
|
|
IF->if_configname.Buffer = (PVOID) (((uchar *)IF) + InterfaceSize);
|
|
IF->if_configname.Length = 0;
|
|
IF->if_configname.MaximumLength = ConfigName->MaximumLength;
|
|
|
|
CTECopyString(
|
|
&(IF->if_configname),
|
|
ConfigName
|
|
);
|
|
|
|
// Find out how many addresses we have, and get the address list.
|
|
AddrList = GetIFAddrList(&NumAddr, Handle);
|
|
|
|
if (AddrList == NULL) {
|
|
CTEFreeMem(IF);
|
|
goto failure;
|
|
}
|
|
|
|
//
|
|
//Link this interface onto the global interface list
|
|
//
|
|
IF->if_next = IFList;
|
|
IFList = IF;
|
|
|
|
if (FirstIF == NULL)
|
|
FirstIF = IF;
|
|
|
|
NumIF++;
|
|
IndexMask = 1;
|
|
|
|
for (i = 0; i < MAX_TDI_ENTITIES; i++) {
|
|
if ((IFBitMask[i/BITS_PER_WORD] & IndexMask) == 0) {
|
|
IFIndex = i+ 1;
|
|
IFBitMask[i/BITS_PER_WORD] |= IndexMask;
|
|
break;
|
|
}
|
|
if (((i+1) % BITS_PER_WORD) == 0) {
|
|
IndexMask = 1;
|
|
} else {
|
|
IndexMask = IndexMask << 1;
|
|
}
|
|
}
|
|
|
|
if (i == MAX_TDI_ENTITIES) {
|
|
// Too many interfaces bound.
|
|
goto failure;
|
|
}
|
|
|
|
IF->if_index = IFIndex;
|
|
|
|
// Now loop through, initializing each NTE as we go. We don't hold any
|
|
// locks while we do this, since NDIS won't reenter us here and no one
|
|
// else manipulates the NetTableList.
|
|
|
|
for (i = 0;i < NumAddr;i++) {
|
|
NetTableEntry *PrevNTE;
|
|
IPAddr NewAddr;
|
|
uint isPrimary;
|
|
|
|
if (i == 0) {
|
|
isPrimary = TRUE;
|
|
}
|
|
else {
|
|
isPrimary = FALSE;
|
|
}
|
|
|
|
NTE = IPAddNTE(
|
|
&GConfigInfo,
|
|
PNPContext,
|
|
RegRtn,
|
|
BindInfo,
|
|
IF,
|
|
net_long(AddrList[i].ial_addr),
|
|
net_long(AddrList[i].ial_mask),
|
|
isPrimary,
|
|
FALSE // not dynamic
|
|
);
|
|
|
|
if (NTE == NULL) {
|
|
goto failure;
|
|
}
|
|
|
|
if (isPrimary) {
|
|
PrimaryNTE = NTE;
|
|
|
|
#ifdef NT
|
|
|
|
//
|
|
// Write the context of the first interface to the registry.
|
|
//
|
|
if (isPrimary) {
|
|
NTSTATUS writeStatus;
|
|
ulong context = (ulong) NTE->nte_context;
|
|
|
|
writeStatus = SetRegDWORDValue(
|
|
Handle,
|
|
L"IPInterfaceContext",
|
|
&context
|
|
);
|
|
|
|
if (!NT_SUCCESS(writeStatus)) {
|
|
CTELogEvent(
|
|
IPDriverObject,
|
|
EVENT_TCPIP_DHCP_INIT_FAILED,
|
|
2,
|
|
1,
|
|
&(ConfigName->Buffer),
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
TCPTRACE((
|
|
"IP: Unable to write IPInterfaceContext value for adapter %ws\n"
|
|
" (status %lx). DHCP will be unable to configure this \n"
|
|
" adapter.\n",
|
|
ConfigName->Buffer,
|
|
writeStatus
|
|
));
|
|
}
|
|
}
|
|
|
|
#endif // NT
|
|
|
|
}
|
|
|
|
LastNTE = NTE;
|
|
}
|
|
|
|
#ifdef NT
|
|
|
|
if (LastNTE != NULL) {
|
|
|
|
NTSTATUS writeStatus;
|
|
ulong context = (ulong) LastNTE->nte_context;
|
|
|
|
writeStatus = SetRegDWORDValue(
|
|
Handle,
|
|
L"IPInterfaceContextMax",
|
|
&context
|
|
);
|
|
|
|
if (!NT_SUCCESS(writeStatus)) {
|
|
CTELogEvent(
|
|
IPDriverObject,
|
|
EVENT_TCPIP_DHCP_INIT_FAILED,
|
|
3,
|
|
1,
|
|
&(ConfigName->Buffer),
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
TCPTRACE((
|
|
"IP: Unable to write IPInterfaceContextMax value for adapter %ws\n"
|
|
" (status %lx). DHCP will be unable to configure this \n"
|
|
" adapter.\n",
|
|
ConfigName->Buffer,
|
|
writeStatus
|
|
));
|
|
}
|
|
}
|
|
|
|
#endif // NT
|
|
|
|
CloseIFConfig(Handle);
|
|
|
|
// We've initialized our NTEs. Now get the adapter open, and go through
|
|
// again, calling DHCP if we need to.
|
|
|
|
(*(BindInfo->lip_open))(BindInfo->lip_context);
|
|
|
|
if (PrimaryNTE != NULL) {
|
|
#ifdef CHICAGO
|
|
NotifyInterfaceChange(PrimaryNTE->nte_context, TRUE);
|
|
#endif
|
|
}
|
|
|
|
// Now walk through the NTEs we've added, and get addresses for them (or
|
|
// tell clients about them). This code assumes that no one else has mucked
|
|
// with the list while we're here.
|
|
for (i = 0; i < NumAddr; i++, NTE = NTE->nte_next) {
|
|
|
|
//
|
|
// BUGBUG - Doesn't this send up a notification of zero for a DHCP'd
|
|
// address on chicago??? (mikemas, 2/5/96)
|
|
//
|
|
#ifdef SECFLTR
|
|
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
|
|
NTE->nte_context, &NTE->nte_addrhandle, &(IF->if_configname), TRUE);
|
|
|
|
#else // SECFLTR
|
|
|
|
NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext,
|
|
NTE->nte_context, &NTE->nte_addrhandle, NULL, TRUE);
|
|
|
|
#endif // SECFLTR
|
|
|
|
if (IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
|
|
// Call DHCP to get an address for this guy.
|
|
#ifdef CHICAGO
|
|
RequestDHCPAddr(NTE->nte_context);
|
|
#endif
|
|
} else {
|
|
InitIGMPForNTE(NTE);
|
|
}
|
|
}
|
|
|
|
|
|
CTEFreeMem(AddrList);
|
|
return IP_SUCCESS;
|
|
|
|
failure:
|
|
CloseIFConfig(Handle);
|
|
|
|
if (AddrList != NULL)
|
|
CTEFreeMem(AddrList);
|
|
|
|
return IP_GENERAL_FAILURE;
|
|
}
|
|
|
|
extern uint BCastMinMTU;
|
|
|
|
|
|
//* IPDelNTE - Delete an active NTE
|
|
//
|
|
// Called to delete an active NTE from the system. The RouteTableLock
|
|
// must be acquired before calling this routine. It will be freed upon
|
|
// return.
|
|
//
|
|
// Input: NTE - A pointer to the network entry to delete.
|
|
// RouteTableHandle - A pointer to the lock handle for the
|
|
// route table lock, which the caller has
|
|
// acquired.
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
void
|
|
IPDelNTE(NetTableEntry *NTE, CTELockHandle *RouteTableHandle)
|
|
{
|
|
Interface *IF = NTE->nte_if;
|
|
ReassemblyHeader *RH, *RHNext;
|
|
EchoControl *EC, *ECNext;
|
|
EchoRtn Rtn;
|
|
CTELockHandle Handle;
|
|
PNDIS_PACKET Packet;
|
|
PNDIS_BUFFER Buffer;
|
|
uchar *TDBuffer;
|
|
|
|
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
(void) IPpSetNTEAddr(NTE, NULL_IP_ADDR, NULL_IP_ADDR, RouteTableHandle, NULL, NULL);
|
|
|
|
} else {
|
|
CTEFreeLock(&RouteTableLock, *RouteTableHandle);
|
|
|
|
NotifyAddrChange(NULL_IP_ADDR, NULL_IP_ADDR,
|
|
NTE->nte_pnpcontext, NTE->nte_context,
|
|
&NTE->nte_addrhandle, NULL, FALSE);
|
|
}
|
|
|
|
CTEGetLock(&RouteTableLock, RouteTableHandle);
|
|
|
|
if (DHCPNTE == NTE)
|
|
DHCPNTE = NULL;
|
|
|
|
NTE->nte_flags = 0;
|
|
|
|
CTEFreeLock(&RouteTableLock, *RouteTableHandle);
|
|
|
|
CTEStopTimer(&NTE->nte_timer);
|
|
|
|
CTEGetLock(&NTE->nte_lock, &Handle);
|
|
|
|
RH = NTE->nte_ralist;
|
|
NTE->nte_ralist = NULL;
|
|
EC = NTE->nte_echolist;
|
|
NTE->nte_echolist = NULL;
|
|
|
|
CTEFreeLock(&NTE->nte_lock, Handle);
|
|
|
|
// Free any reassembly resources.
|
|
while (RH != NULL) {
|
|
RHNext = RH->rh_next;
|
|
FreeRH(RH);
|
|
RH = RHNext;
|
|
}
|
|
|
|
// Now free any pending echo requests.
|
|
while (EC != NULL) {
|
|
ECNext= EC->ec_next;
|
|
Rtn = (EchoRtn)EC->ec_rtn;
|
|
(*Rtn)(EC, IP_ADDR_DELETED, NULL, 0, NULL);
|
|
EC = ECNext;
|
|
}
|
|
|
|
//
|
|
// Free the TD resource allocated for this NTE.
|
|
//
|
|
CTEGetLock(&(IF->if_lock), &Handle);
|
|
|
|
Packet = IF->if_tdpacket;
|
|
|
|
if (Packet != NULL) {
|
|
|
|
IF->if_tdpacket =
|
|
((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link;
|
|
|
|
CTEFreeLock(&(IF->if_lock), Handle);
|
|
|
|
Buffer = Packet->Private.Head;
|
|
TDBuffer = NdisBufferVirtualAddress(Buffer);
|
|
NdisFreePacketPool(Packet->Private.Pool);
|
|
|
|
#ifdef CHICAGO
|
|
NdisFreeBufferPool(Buffer->Pool);
|
|
#endif
|
|
CTEFreeMem(TDBuffer);
|
|
}
|
|
else {
|
|
CTEFreeLock(&(IF->if_lock), Handle);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//* IPDeleteDynamicNTE - Deletes a "dynamic" NTE.
|
|
//
|
|
// Called to delete a network entry which was dynamically created on an
|
|
// existing interface.
|
|
//
|
|
// Input: NTEContext - The context value identifying the NTE to delete.
|
|
//
|
|
// Returns: Nonzero if the operation succeeded. Zero if it failed.
|
|
//
|
|
uint
|
|
IPDeleteDynamicNTE(ushort NTEContext)
|
|
{
|
|
NetTableEntry *NTE;
|
|
Interface *IF;
|
|
CTELockHandle Handle;
|
|
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if ( (NTE->nte_context == NTEContext) &&
|
|
(NTE->nte_flags & NTE_DYNAMIC) &&
|
|
(NTE->nte_flags & NTE_ACTIVE)
|
|
)
|
|
{
|
|
CTEAssert(NTE != LoopNTE);
|
|
CTEAssert(!(NTE->nte_flags & NTE_PRIMARY));
|
|
|
|
IPDelNTE(NTE, &Handle);
|
|
|
|
//
|
|
// Route table lock was freed by IPDelNTE
|
|
//
|
|
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//* IPGetNTEInfo - Retrieve information about a network entry.
|
|
//
|
|
// Called to retrieve context information about a network entry.
|
|
//
|
|
// Input: NTEContext - The context value which identifies the NTE to query.
|
|
//
|
|
// Output: NTEInstance - The instance number associated with the NTE.
|
|
// Address - The address assigned to the NTE.
|
|
// SubnetMask - The subnet mask assigned to the NTE.
|
|
// NTEFlags - The flag values associated with the NTE.
|
|
//
|
|
// Returns: Nonzero if the operation succeeded. Zero if it failed.
|
|
//
|
|
uint
|
|
IPGetNTEInfo(ushort NTEContext, ulong *NTEInstance, IPAddr *Address,
|
|
IPMask *SubnetMask, ushort *NTEFlags)
|
|
{
|
|
NetTableEntry *NTE;
|
|
CTELockHandle Handle;
|
|
uint retval = FALSE;
|
|
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if ((NTE->nte_context == NTEContext) &&
|
|
(NTE->nte_flags & NTE_ACTIVE)
|
|
)
|
|
{
|
|
*NTEInstance = NTE->nte_instance;
|
|
|
|
if (NTE->nte_flags & NTE_VALID) {
|
|
*Address = NTE->nte_addr;
|
|
*SubnetMask = NTE->nte_mask;
|
|
}
|
|
else {
|
|
*Address = NULL_IP_ADDR;
|
|
*SubnetMask = NULL_IP_ADDR;
|
|
}
|
|
|
|
*NTEFlags = NTE->nte_flags;
|
|
retval = TRUE;
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
|
|
return(retval);
|
|
}
|
|
|
|
|
|
//* IPDelInterface - Delete an interface.
|
|
//
|
|
// Called when we need to delete an interface that's gone away. We'll walk
|
|
// the NTE list, looking for NTEs that are on the interface that's going
|
|
// away. For each of those, we'll invalidate the NTE, delete routes on it,
|
|
// and notify the upper layers that it's gone. When that's done we'll pull
|
|
// the interface out of the list and free the memory.
|
|
//
|
|
// Note that this code probably isn't MP safe. We'll need to fix that for
|
|
// the port to NT.
|
|
//
|
|
// Input: Context - Pointer to primary NTE on the interface.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
IPDelInterface(void *Context)
|
|
{
|
|
NetTableEntry *NTE = (NetTableEntry *)Context;
|
|
NetTableEntry *FoundNTE = NULL;
|
|
Interface *IF, *PrevIF;
|
|
CTELockHandle Handle;
|
|
PNDIS_PACKET Packet;
|
|
PNDIS_BUFFER Buffer;
|
|
uchar *TDBuffer;
|
|
ReassemblyHeader *RH;
|
|
EchoControl *EC;
|
|
EchoRtn Rtn;
|
|
CTEBlockStruc Block;
|
|
|
|
IF = NTE->nte_if;
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
|
|
IF->if_flags |= IF_FLAGS_DELETING;
|
|
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if (NTE->nte_if == IF) {
|
|
|
|
if (FoundNTE == NULL) {
|
|
FoundNTE = NTE;
|
|
}
|
|
|
|
// This guy is on the interface, and needs to be deleted.
|
|
IPDelNTE(NTE, &Handle);
|
|
|
|
CTEGetLock(&RouteTableLock, &Handle);
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&RouteTableLock, Handle);
|
|
|
|
// Clear this index from the IFBitMask.
|
|
CTEAssert(IFBitMask[(IF->if_index-1)/BITS_PER_WORD] & (1 << ((IF->if_index - 1)%BITS_PER_WORD)));
|
|
|
|
IFBitMask[(IF->if_index-1)/BITS_PER_WORD] &= ~(1 << ((IF->if_index - 1)%BITS_PER_WORD));
|
|
|
|
if (FoundNTE != NULL) {
|
|
#ifdef CHICAGO
|
|
NotifyInterfaceChange(FoundNTE->nte_context, FALSE);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Free the TD resources on the IF.
|
|
//
|
|
|
|
while ((Packet = IF->if_tdpacket) != NULL) {
|
|
|
|
IF->if_tdpacket =
|
|
((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link;
|
|
|
|
Buffer = Packet->Private.Head;
|
|
TDBuffer = NdisBufferVirtualAddress(Buffer);
|
|
NdisFreePacketPool(Packet->Private.Pool);
|
|
|
|
#ifdef CHICAGO
|
|
NdisFreeBufferPool(Buffer->Pool);
|
|
#endif
|
|
CTEFreeMem(TDBuffer);
|
|
}
|
|
|
|
// If this was the 'first' IF, set that to NULL and delete the broadcast
|
|
// route that goes through him.
|
|
if (FirstIF == IF) {
|
|
DeleteRoute(IP_LOCAL_BCST, HOST_MASK, IPADDR_LOCAL,
|
|
FirstIF);
|
|
DeleteRoute(IP_ZERO_BCST, HOST_MASK, IPADDR_LOCAL,
|
|
FirstIF);
|
|
FirstIF = NULL;
|
|
BCastMinMTU = 0xffff;
|
|
}
|
|
|
|
// OK, we've cleaned up all the routes through this guy.
|
|
// Get ready to block waiting for all reference to go
|
|
// away, then dereference our reference. After this, go
|
|
// ahead and try to block. Mostly likely our reference was
|
|
// the last one, so we won't block - we'll wake up immediately.
|
|
CTEInitBlockStruc(&Block);
|
|
IF->if_block = &Block;
|
|
|
|
DerefIF(IF);
|
|
|
|
(void)CTEBlock(&Block);
|
|
|
|
// OK, we've cleaned up all references, so there shouldn't be
|
|
// any more transmits pending through this interface. Close the
|
|
// adapter to force synchronization with any receives in process.
|
|
|
|
|
|
(*(IF->if_close))(IF->if_lcontext);
|
|
|
|
// Now walk the IFList, looking for this guy. When we find him, free him.
|
|
PrevIF = STRUCT_OF(Interface, &IFList, if_next);
|
|
while (PrevIF->if_next != IF && PrevIF->if_next != NULL)
|
|
PrevIF = PrevIF->if_next;
|
|
|
|
if (PrevIF->if_next != NULL) {
|
|
PrevIF->if_next = IF->if_next;
|
|
NumIF--;
|
|
CTEFreeMem(IF);
|
|
} else
|
|
CTEAssert(FALSE);
|
|
|
|
// If we've deleted the first interface but still have other valid
|
|
// interfaces, we need to create a new FirstIF and read broadcast routes
|
|
// through it. NumIF is always at least one because of the loopback
|
|
// interface.
|
|
if (FirstIF == NULL && NumIF != 1) {
|
|
|
|
FirstIF = IFList;
|
|
|
|
for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) {
|
|
if ((NTE->nte_flags & NTE_VALID) && NTE != LoopNTE) {
|
|
BCastMinMTU = MIN(BCastMinMTU, NTE->nte_mss);
|
|
AddRoute(NTE->nte_if->if_bcast, HOST_MASK, IPADDR_LOCAL,
|
|
FirstIF, BCastMinMTU, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#else // _PNP_POWER
|
|
|
|
|
|
//* NotifyAddrChange - Notify clients of a change in addresses.
|
|
//
|
|
// Called when we want to notify registered clients that an address has come
|
|
// or gone. We call TDI to perform this function.
|
|
//
|
|
// Input: Addr - Addr that has changed.
|
|
// Mask - Ignored - Mask that has changed.
|
|
// Context - Ignored - PNP context for address
|
|
// IPContext - NTE context for NTE
|
|
// Handle - Pointer to where to get/set address registration
|
|
// handle
|
|
// ConfigName - Registry name to use to retrieve config info.
|
|
// Added - True if the addr is coming, False if it's going.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext,
|
|
PVOID *Handle, PNDIS_STRING ConfigName, uint Added)
|
|
|
|
{
|
|
IP_STATUS StatusType;
|
|
NDIS_HANDLE ConfigHandle = NULL;
|
|
int i;
|
|
ULStatusProc StatProc;
|
|
|
|
|
|
if (Added) {
|
|
StatusType = IP_ADDR_ADDED;
|
|
|
|
#ifdef SECFLTR
|
|
//
|
|
// Open a configuration key
|
|
//
|
|
if (!OpenIFConfig(ConfigName, &ConfigHandle)) {
|
|
//
|
|
// Not much we can do. The transports will have
|
|
// to handle this.
|
|
//
|
|
CTEAssert(ConfigHandle == NULL);
|
|
}
|
|
#endif // SECFLTR
|
|
|
|
}
|
|
else {
|
|
StatusType = IP_ADDR_DELETED;
|
|
}
|
|
|
|
for ( i = 0; i < NextPI; i++) {
|
|
StatProc = IPProtInfo[i].pi_status;
|
|
if (StatProc != NULL)
|
|
(*StatProc)(IP_HW_STATUS, StatusType, Addr, NULL_IP_ADDR,
|
|
NULL_IP_ADDR, 0, ConfigHandle );
|
|
}
|
|
|
|
#ifdef SECFLTR
|
|
|
|
if (ConfigHandle != NULL) {
|
|
CloseIFConfig(ConfigHandle);
|
|
}
|
|
|
|
#endif // SECFLTR
|
|
|
|
}
|
|
|
|
|
|
#endif // _PNP_POWER
|
|
|
|
|
|
#pragma BEGIN_INIT
|
|
|
|
//** ipinit - Initialize ourselves.
|
|
//
|
|
// This routine is called during initialization from the OS-specific
|
|
// init code. We need to check for the presence of the common xport
|
|
// environment first.
|
|
//
|
|
//
|
|
// Entry: Nothing.
|
|
//
|
|
// Returns: 0 if initialization failed, non-zero if it succeeds.
|
|
//
|
|
int
|
|
IPInit()
|
|
{
|
|
IPConfigInfo *ci; // Pointer to our IP configuration info.
|
|
int numnets; // Number of nets active.
|
|
int i;
|
|
uint j; // Counter variables.
|
|
NetTableEntry *nt; // Pointer to current NTE.
|
|
LLIPBindInfo *ARPInfo; // Info. returned from ARP.
|
|
NDIS_STATUS Status;
|
|
Interface *NetInterface; // Interface for a particular net.
|
|
LLIPRegRtn RegPtr;
|
|
NetTableEntry *lastNTE;
|
|
|
|
|
|
if (!CTEInitialize())
|
|
return IP_INIT_FAILURE;
|
|
|
|
CTERefillMem();
|
|
|
|
if ((ci = IPGetConfig()) == NULL)
|
|
return IP_INIT_FAILURE;
|
|
|
|
#ifndef _PNP_POWER
|
|
MaxIPNets = ci->ici_numnets + 1;
|
|
#endif // _PNP_POWER
|
|
|
|
for (ATCIndex=0; ATCIndex < ATC_SIZE; ATCIndex++) {
|
|
ATCache[ATCIndex].atc_flags = 0;
|
|
}
|
|
ATCIndex = 0;
|
|
|
|
// First, initalize our loopback stuff.
|
|
NetTableList = InitLoopback(ci);
|
|
if (NetTableList == NULL)
|
|
return IP_INIT_FAILURE;
|
|
|
|
if (!ARPInit()) {
|
|
CTEFreeMem(NetTableList);
|
|
return IP_INIT_FAILURE; // Couldn't initialize ARP.
|
|
}
|
|
|
|
CTERefillMem();
|
|
if (!InitRouting(ci)) {
|
|
CTEFreeMem(NetTableList);
|
|
return IP_INIT_FAILURE;
|
|
}
|
|
|
|
RATimeout = DEFAULT_RA_TIMEOUT;
|
|
#if 0
|
|
CTEInitLock(&PILock);
|
|
#endif
|
|
LastPI = IPProtInfo;
|
|
|
|
|
|
if (!ci->ici_gateway)
|
|
InterfaceSize = sizeof(Interface);
|
|
else
|
|
InterfaceSize = sizeof(RouteInterface);
|
|
|
|
DeadGWDetect = ci->ici_deadgwdetect;
|
|
PMTUDiscovery = ci->ici_pmtudiscovery;
|
|
IGMPLevel = ci->ici_igmplevel;
|
|
DefaultTTL = MIN(ci->ici_ttl, 255);
|
|
DefaultTOS = ci->ici_tos & 0xfc;
|
|
if (IGMPLevel > 2)
|
|
IGMPLevel = 0;
|
|
|
|
InitTimestamp();
|
|
|
|
#ifndef _PNP_POWER
|
|
numnets = ci->ici_numnets;
|
|
|
|
lastNTE = NetTableList; // loopback is only one on the list
|
|
CTEAssert(lastNTE != NULL);
|
|
CTEAssert(lastNTE->nte_next == NULL);
|
|
|
|
// Loop through the config. info, copying the addresses and masks.
|
|
for (i = 0; i < numnets; i++) {
|
|
|
|
CTERefillMem();
|
|
nt = CTEAllocMem(sizeof(NetTableEntry));
|
|
if (nt == NULL)
|
|
continue;
|
|
|
|
CTEMemSet(nt, 0, sizeof(NetTableEntry));
|
|
|
|
nt->nte_addr = net_long(ci->ici_netinfo[i].nci_addr);
|
|
nt->nte_mask = net_long(ci->ici_netinfo[i].nci_mask);
|
|
nt->nte_mss = MAX(ci->ici_netinfo[i].nci_mtu, 68);
|
|
nt->nte_flags = (IP_ADDR_EQUAL(nt->nte_addr, NULL_IP_ADDR) ? 0 :
|
|
NTE_VALID);
|
|
nt->nte_flags |= NTE_ACTIVE;
|
|
|
|
CTEInitLock(&nt->nte_lock);
|
|
// If the address is invalid, skip it.
|
|
if (CLASSD_ADDR(nt->nte_addr) || CLASSE_ADDR(nt->nte_addr)) {
|
|
CTEFreeMem(nt);
|
|
continue;
|
|
}
|
|
|
|
// See if we're already bound to this adapter. If we are, use the same
|
|
// interface. Otherwise assign a new one. We assume that the loopback
|
|
// interface is IF 1, so there is one less than NumIF in the table.
|
|
for (j = 0; j < NumIF - 1; j++) {
|
|
if (CTEEqualString(&(AdptNameTable[j].nm_name),
|
|
&(ci->ici_netinfo[i].nci_name))) {
|
|
|
|
// Names match. Now check driver/types.
|
|
if (((ci->ici_netinfo[i].nci_type == NET_TYPE_LAN) &&
|
|
(AdptNameTable[j].nm_driver.Buffer == NULL)) ||
|
|
(CTEEqualString(&(AdptNameTable[j].nm_driver),
|
|
&(ci->ici_netinfo[i].nci_driver))))
|
|
break; // Found a match
|
|
}
|
|
}
|
|
|
|
if (j < (NumIF - 1)) {
|
|
|
|
// Found a match above, so use that interface.
|
|
CTERefillMem();
|
|
nt->nte_if = AdptNameTable[j].nm_interface;
|
|
ARPInfo = AdptNameTable[j].nm_arpinfo;
|
|
// If the Init of the interface or the NTE fails, we don't want to
|
|
// close the interface, because another net is using it.
|
|
|
|
if (!InitInterface(nt)) {
|
|
CTEFreeMem(nt);
|
|
continue;
|
|
}
|
|
if (!InitNTE(nt)) {
|
|
CTEFreeMem(nt);
|
|
continue;
|
|
}
|
|
|
|
} else { // No match, create a new interface
|
|
|
|
CTEAssert(NumIF <= MaxIPNets);
|
|
|
|
if (NumIF == MaxIPNets) {
|
|
continue; // too many adapters
|
|
}
|
|
|
|
CTERefillMem();
|
|
|
|
ARPInfo = CTEAllocMem(sizeof(LLIPBindInfo));
|
|
|
|
if (ARPInfo == NULL) {
|
|
CTEFreeMem(nt);
|
|
continue;
|
|
}
|
|
|
|
NetInterface = CTEAllocMem(
|
|
InterfaceSize +
|
|
ci->ici_netinfo[i].nci_configname.MaximumLength
|
|
);
|
|
|
|
if (!NetInterface) {
|
|
CTEFreeMem(ARPInfo);
|
|
CTEFreeMem(nt);
|
|
continue;
|
|
}
|
|
|
|
CTEMemSet(NetInterface, 0, InterfaceSize);
|
|
|
|
nt->nte_if = NetInterface;
|
|
nt->nte_flags |= NTE_PRIMARY; // He is the primary NTE.
|
|
|
|
CTEInitLock(&NetInterface->if_lock);
|
|
|
|
if (ci->ici_gateway) {
|
|
// Hack in the max pending value here. Probably should be
|
|
// done in iproute.c, but it's easier to do it here.
|
|
|
|
RouteInterface *RtIF;
|
|
|
|
RtIF = (RouteInterface *)NetInterface;
|
|
RtIF->ri_q.rsq_maxpending = ci->ici_netinfo[i].nci_maxpending;
|
|
}
|
|
|
|
// If this is a LAN, register with ARP.
|
|
if (ci->ici_netinfo[i].nci_type == NET_TYPE_LAN)
|
|
RegPtr = ARPRegister;
|
|
else
|
|
RegPtr = FindRegPtr(&ci->ici_netinfo[i].nci_driver);
|
|
|
|
if (RegPtr == NULL || !((*RegPtr)(&ci->ici_netinfo[i].nci_name,
|
|
nt, IPRcv, IPSendComplete, IPStatus, IPTDComplete,
|
|
IPRcvComplete, ARPInfo, NumIF))) {
|
|
CTEFreeMem(ARPInfo);
|
|
CTEFreeMem(NetInterface);
|
|
CTEFreeMem(nt);
|
|
continue; // We're hosed, skip this net.
|
|
}
|
|
else {
|
|
|
|
if (ci->ici_netinfo[i].nci_zerobcast)
|
|
NetInterface->if_bcast = IP_ZERO_BCST;
|
|
else
|
|
NetInterface->if_bcast = IP_LOCAL_BCST;
|
|
|
|
NetInterface->if_xmit = ARPInfo->lip_transmit;
|
|
NetInterface->if_transfer = ARPInfo->lip_transfer;
|
|
NetInterface->if_close = ARPInfo->lip_close;
|
|
NetInterface->if_invalidate = ARPInfo->lip_invalidate;
|
|
NetInterface->if_lcontext = ARPInfo->lip_context;
|
|
NetInterface->if_addaddr = ARPInfo->lip_addaddr;
|
|
NetInterface->if_deladdr = ARPInfo->lip_deladdr;
|
|
NetInterface->if_qinfo = ARPInfo->lip_qinfo;
|
|
NetInterface->if_setinfo = ARPInfo->lip_setinfo;
|
|
NetInterface->if_getelist = ARPInfo->lip_getelist;
|
|
NetInterface->if_tdpacket = NULL;
|
|
NetInterface->if_index = ARPInfo->lip_index;
|
|
NetInterface->if_mtu = ARPInfo->lip_mss - sizeof(IPHeader);
|
|
NetInterface->if_speed = ARPInfo->lip_speed;
|
|
NetInterface->if_flags = ARPInfo->lip_flags & LIP_P2P_FLAG ?
|
|
IF_FLAGS_P2P : 0;
|
|
NetInterface->if_addrlen = ARPInfo->lip_addrlen;
|
|
NetInterface->if_addr = ARPInfo->lip_addr;
|
|
NetInterface->if_pnpcontext = PNPContext;
|
|
NetInterface->if_llipflags = ArpInfo->lip_flags
|
|
|
|
NetInterface->if_configname.Buffer =
|
|
(PVOID) (((uchar *)NetInterface) + InterfaceSize);
|
|
|
|
NetInterface->if_configname.Length = 0;
|
|
NetInterface->if_configname.MaximumLength =
|
|
ci->ici_netinfo[i].nci_configname.MaximumLength;
|
|
|
|
CTECopyString(
|
|
&(NetInterface->if_configname),
|
|
&(ci->ici_netinfo[i].nci_configname)
|
|
);
|
|
|
|
CTERefillMem();
|
|
|
|
if (!InitInterface(nt)) {
|
|
CTEFreeMem(ARPInfo);
|
|
CTEFreeMem(NetInterface);
|
|
CTEFreeMem(nt);
|
|
continue;
|
|
}
|
|
|
|
if (!InitNTE(nt)) {
|
|
CTEFreeMem(ARPInfo);
|
|
CTEFreeMem(NetInterface);
|
|
CTEFreeMem(nt);
|
|
continue;
|
|
}
|
|
|
|
CTERefillMem();
|
|
if (!CTEAllocateString(&AdptNameTable[j].nm_name,
|
|
CTELengthString(&ci->ici_netinfo[i].nci_name))) {
|
|
CTEFreeMem(ARPInfo);
|
|
CTEFreeMem(NetInterface);
|
|
CTEFreeMem(nt);
|
|
continue;
|
|
}
|
|
|
|
if (ci->ici_netinfo[i].nci_type != NET_TYPE_LAN) {
|
|
if (!CTEAllocateString(&AdptNameTable[j].nm_driver,
|
|
CTELengthString(&ci->ici_netinfo[i].nci_driver))) {
|
|
CTEFreeString(&AdptNameTable[j].nm_name);
|
|
CTEFreeMem(ARPInfo);
|
|
CTEFreeMem(NetInterface);
|
|
CTEFreeMem(nt);
|
|
continue;
|
|
}
|
|
CTECopyString(&(AdptNameTable[j].nm_driver),
|
|
&(ci->ici_netinfo[i].nci_driver));
|
|
}
|
|
|
|
CTECopyString(&(AdptNameTable[j].nm_name),
|
|
&(ci->ici_netinfo[i].nci_name));
|
|
AdptNameTable[j].nm_interface = NetInterface;
|
|
AdptNameTable[j].nm_arpinfo = ARPInfo;
|
|
NetInterface->if_next = IFList;
|
|
IFList = NetInterface;
|
|
if (FirstIF == NULL)
|
|
FirstIF = NetInterface;
|
|
NumIF++;
|
|
|
|
#ifdef NT
|
|
//
|
|
// Write the interface context to the registry for DHCP et al
|
|
//
|
|
if (ci->ici_netinfo[i].nci_reghandle != NULL) {
|
|
NTSTATUS writeStatus;
|
|
ulong context = (ulong) nt->nte_context;
|
|
|
|
writeStatus = SetRegDWORDValue(
|
|
ci->ici_netinfo[i].nci_reghandle,
|
|
L"IPInterfaceContext",
|
|
&context
|
|
);
|
|
|
|
if (!NT_SUCCESS(writeStatus)) {
|
|
CTELogEvent(
|
|
IPDriverObject,
|
|
EVENT_TCPIP_DHCP_INIT_FAILED,
|
|
2,
|
|
1,
|
|
&(ci->ici_netinfo[i].nci_name.Buffer),
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
TCPTRACE((
|
|
"IP: Unable to write IPInterfaceContext value for adapter %ws\n"
|
|
" (status %lx). DHCP will be unable to configure this \n"
|
|
" adapter.\n",
|
|
ci->ici_netinfo[i].nci_name.Buffer,
|
|
writeStatus
|
|
));
|
|
}
|
|
}
|
|
#endif // NT
|
|
}
|
|
}
|
|
|
|
nt->nte_next = NULL;
|
|
lastNTE->nte_next = nt;
|
|
lastNTE = nt;
|
|
NumNTE++;
|
|
|
|
if (!InitNTERouting(nt, ci->ici_netinfo[i].nci_numgws,
|
|
ci->ici_netinfo[i].nci_gw)) {
|
|
// Couldn't add the routes for this NTE. Mark has as not valid.
|
|
// Probably should log an event here.
|
|
if (nt->nte_flags & NTE_VALID) {
|
|
nt->nte_flags &= ~NTE_VALID;
|
|
nt->nte_if->if_ntecount--;
|
|
}
|
|
}
|
|
|
|
#ifdef NT
|
|
if (!IP_ADDR_EQUAL(nt->nte_addr, NULL_IP_ADDR)) {
|
|
SetPersistentRoutesForNTE(
|
|
net_long(nt->nte_addr),
|
|
net_long(nt->nte_mask),
|
|
nt->nte_if->if_index
|
|
);
|
|
}
|
|
#endif // NT
|
|
|
|
}
|
|
|
|
#endif // ndef PNP_POWER
|
|
|
|
if (NumNTE != 0) { // We have an NTE, and loopback initialized.
|
|
PNDIS_PACKET Packet;
|
|
|
|
#ifdef _PNP_POWER
|
|
for (i=0; i<MAX_TDI_ENTITIES; i++) {
|
|
IFBitMask[i/BITS_PER_WORD] = 0;
|
|
}
|
|
IFBitMask[0] = 1;
|
|
#endif
|
|
|
|
IPSInfo.ipsi_forwarding = (ci->ici_gateway ? IP_FORWARDING :
|
|
IP_NOT_FORWARDING);
|
|
IPSInfo.ipsi_defaultttl = DefaultTTL;
|
|
IPSInfo.ipsi_reasmtimeout = DEFAULT_RA_TIMEOUT;
|
|
|
|
// Allocate our packet pools.
|
|
CTEInitLock(&HeaderLock);
|
|
#ifdef NT
|
|
ExInitializeSListHead(&PacketList);
|
|
ExInitializeSListHead(&HdrBufList);
|
|
#endif
|
|
|
|
|
|
Packet = GrowIPPacketList();
|
|
|
|
if (Packet == NULL) {
|
|
CloseNets();
|
|
FreeNets();
|
|
IPFreeConfig(ci);
|
|
return IP_INIT_FAILURE;
|
|
}
|
|
|
|
(void)FreeIPPacket(Packet);
|
|
|
|
NdisAllocateBufferPool(&Status, &BufferPool, NUM_IP_NONHDR_BUFFERS);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
#ifdef DEBUG
|
|
DEBUGCHK;
|
|
#endif
|
|
}
|
|
|
|
CTERefillMem();
|
|
|
|
ICMPInit(DEFAULT_ICMP_BUFFERS);
|
|
if (!IGMPInit())
|
|
IGMPLevel = 1;
|
|
|
|
// Should check error code, and log an event here if this fails.
|
|
CTERefillMem();
|
|
InitGateway(ci);
|
|
|
|
IPFreeConfig(ci);
|
|
CTERefillMem();
|
|
|
|
#ifndef _PNP_POWER
|
|
OpenAdapters();
|
|
CleanAdaptTable(); // Clean up the adapter info we don't need.
|
|
#endif
|
|
|
|
CTERefillMem();
|
|
|
|
// Loop through, initialize IGMP for each NTE.
|
|
for (nt = NetTableList; nt != NULL; nt = nt->nte_next)
|
|
InitIGMPForNTE(nt);
|
|
|
|
return IP_INIT_SUCCESS;
|
|
}
|
|
else {
|
|
FreeNets();
|
|
IPFreeConfig(ci);
|
|
return IP_INIT_FAILURE; // Couldn't initialize anything.
|
|
}
|
|
}
|
|
|
|
#pragma END_INIT
|
|
|
|
|