/*++ Copyright (c) 1990-2000 Microsoft Corporation Module Name: info.c - Routines for querying and setting IP information. Abstract: This file contains the code for dealing with Query/Set information calls. Author: [Environment:] kernel mode only [Notes:] optional-notes Revision History: --*/ #include "precomp.h" #include "info.h" #include "iproute.h" #include "igmp.h" #include "iprtdef.h" #include "arpdef.h" #include "ntddndis.h" #include "tcpipbuf.h" extern NDIS_HANDLE BufferPool; extern Interface *IFList; extern NetTableEntry **NewNetTableList; // hash table for NTEs extern uint NET_TABLE_SIZE; extern uint LoopIndex; // Index of loopback I/F. extern uint DefaultTTL; extern uint NumIF; extern uint NumNTE; extern uint NumActiveNTE; extern RouteInterface DummyInterface; // Dummy interface. extern NetTableEntry *LoopNTE; // Pointer to loopback NTE extern uint RTEReadNext(void *Context, void *Buffer); extern uint RTValidateContext(void *Context, uint * Valid); extern uint RTReadNext(void *Context, void *Buffer); extern uint RTRead(void *Context, void *Buffer); extern void IPInitOptions(IPOptInfo *); uint IPInstance = INVALID_ENTITY_INSTANCE; uint ICMPInstance = INVALID_ENTITY_INSTANCE; TDIEntityID* IPEntityList = NULL; uint IPEntityCount = 0; #if FFP_SUPPORT FFPDriverStats GlobalStatsInfoPrev = {0}; // Stats from the previous request FFPDriverStats GlobalStatsInfoCurr = {0}; // Stats from the current request #endif // if FFP_SUPPORT #define MIB_IPADDR_PRIMARY 1 //* CopyToNdisSafe - Copy a flat buffer to an NDIS_BUFFER chain. // // A utility function to copy a flat buffer to an NDIS buffer chain. We // assume that the NDIS_BUFFER chain is big enough to hold the copy amount; // in a debug build we'll debugcheck if this isn't true. We return a pointer // to the buffer where we stopped copying, and an offset into that buffer. // This is useful for copying in pieces into the chain. // // Input: DestBuf - Destination NDIS_BUFFER chain. // pNextBuf - Pointer to next buffer in chain to copy into. // SrcBuf - Src flat buffer. // Size - Size in bytes to copy. // StartOffset - Pointer to start of offset into first buffer in // chain. Filled in on return with the offset to // copy into next. // // Returns: TRUE - Successfully copied flat buffer into NDIS_BUFFER chain. // FALSE - Failed to copy entire flat buffer. // BOOLEAN CopyToNdisSafe(PNDIS_BUFFER DestBuf, PNDIS_BUFFER * ppNextBuf, uchar * SrcBuf, uint Size, uint * StartOffset) { uint CopySize; uchar *DestPtr; uint DestSize; uint Offset = *StartOffset; uchar *VirtualAddress; uint Length; ASSERT(DestBuf != NULL); ASSERT(SrcBuf != NULL); TcpipQueryBuffer(DestBuf, &VirtualAddress, &Length, NormalPagePriority); if (VirtualAddress == NULL) { return (FALSE); } ASSERT(Length >= Offset); DestPtr = VirtualAddress + Offset; DestSize = Length - Offset; for (;;) { CopySize = MIN(Size, DestSize); RtlCopyMemory(DestPtr, SrcBuf, CopySize); DestPtr += CopySize; SrcBuf += CopySize; if ((Size -= CopySize) == 0) break; if ((DestSize -= CopySize) == 0) { DestBuf = NDIS_BUFFER_LINKAGE(DestBuf); ASSERT(DestBuf != NULL); TcpipQueryBuffer(DestBuf, &VirtualAddress, &Length, NormalPagePriority); if (VirtualAddress == NULL) { return FALSE; } DestPtr = VirtualAddress; DestSize = Length; } } *StartOffset = (uint) (DestPtr - VirtualAddress); if (ppNextBuf) { *ppNextBuf = DestBuf; } return TRUE; } // this structure is used in IPQueryInfo for IP_MIB_ADDRTABLE_ENTRY_ID typedef struct _INFO_LIST { struct _INFO_LIST *info_next; NetTableEntry *info_nte; } INFO_LIST, *PINFO_LIST; //* FreeInfoList - Free INFO_LIST used in IPQueryInfo for IP_MIB_ADDRTABLE_ENTRY_ID // // Input: Temp - List to be freed // // Returns: Nothing. // void FreeInfoList(PINFO_LIST Temp) { PINFO_LIST NextTemp; PINFO_LIST CurrTemp = Temp; while (CurrTemp) { NextTemp = CurrTemp->info_next; CTEFreeMem(CurrTemp); CurrTemp = NextTemp; } } //* IPQueryInfo - IP query information handler. // // Called by the upper layer when it wants to query information about us. // We take in an ID, a buffer and length, and a context value, and return // whatever information we can. // // Input: ID - Pointer to ID structure. // Buffer - Pointer to buffer chain. // Size - Pointer to size in bytes of buffer. On return, filled // in with bytes read. // Context - Pointer to context value. // // Returns: TDI_STATUS of attempt to read information. // long IPQueryInfo(TDIObjectID * ID, PNDIS_BUFFER Buffer, uint * Size, void *Context) { uint BufferSize = *Size; uint BytesCopied = 0; uint Offset = 0; TDI_STATUS Status; ushort NTEContext; uchar InfoBuff[sizeof(IPRouteEntry)]; IPAddrEntry *AddrEntry; NetTableEntry *CurrentNTE; uint Valid, DataLeft; CTELockHandle Handle; Interface *LowerIF; IPInterfaceInfo *IIIPtr; uint LLID = 0; uint Entity; uint Instance; IPAddr IFAddr; uint i; uint bucket; NetTableEntry *NetTableList; CTELockHandle TableHandle; IPInternalPerCpuStats SumCpuStats; BOOLEAN fStatus; DEBUGMSG(DBG_TRACE && DBG_QUERYINFO, (DTEXT("+IPQueryInfo(%x, %x, %x, %x)\n"), ID, Buffer, Size, Context)); Entity = ID->toi_entity.tei_entity; Instance = ID->toi_entity.tei_instance; // See if it's something we might handle. if (Entity != CL_NL_ENTITY && Entity != ER_ENTITY) { // We need to pass this down to the lower layer. Loop through until // we find one that takes it. If noone does, error out. CTEGetLock(&RouteTableLock.Lock, &TableHandle); LowerIF = IFList; while (LowerIF) { if (LowerIF->if_refcount == 0) { // this interface is about to get deleted // fail the request // we can also skip this interface LowerIF = LowerIF->if_next; continue; } LOCKED_REFERENCE_IF(LowerIF); CTEFreeLock(&RouteTableLock.Lock, TableHandle); // we have freed the routetablelock here // but since we have a refcount on LowerIF, LowerIF can't go away Status = (*LowerIF->if_qinfo) (LowerIF->if_lcontext, ID, Buffer, Size, Context); if (Status != TDI_INVALID_REQUEST) { DerefIF(LowerIF); return Status; } CTEGetLock(&RouteTableLock.Lock, &TableHandle); LockedDerefIF(LowerIF); // LowerIF->if_next can't be freed at this point. LowerIF = LowerIF->if_next; } CTEFreeLock(&RouteTableLock.Lock, TableHandle); // If we get here, noone took it. Return an error. return TDI_INVALID_REQUEST; } if ((Entity == CL_NL_ENTITY && Instance != IPInstance) || Instance != ICMPInstance) return TDI_INVALID_REQUEST; // The request is for us. *Size = 0; // Set to 0 in case of an error. // Make sure it's something we support. if (ID->toi_class == INFO_CLASS_GENERIC) { if (ID->toi_type == INFO_TYPE_PROVIDER && ID->toi_id == ENTITY_TYPE_ID) { // He's trying to see what type we are. if (BufferSize >= sizeof(uint)) { *(uint *) & InfoBuff[0] = (Entity == CL_NL_ENTITY) ? CL_NL_IP : ER_ICMP; fStatus = CopyToNdisSafe(Buffer, NULL, InfoBuff, sizeof(uint), &Offset); if (fStatus == FALSE) { return TDI_NO_RESOURCES; } *Size = sizeof(uint); return TDI_SUCCESS; } else return TDI_BUFFER_TOO_SMALL; } return TDI_INVALID_PARAMETER; } else if (ID->toi_class != INFO_CLASS_PROTOCOL || ID->toi_type != INFO_TYPE_PROVIDER) return TDI_INVALID_PARAMETER; // If it's ICMP, just copy the statistics. if (Entity == ER_ENTITY) { // It is ICMP. Make sure the ID is valid. if (ID->toi_id != ICMP_MIB_STATS_ID) return TDI_INVALID_PARAMETER; // He wants the stats. Copy what we can. if (BufferSize < sizeof(ICMPSNMPInfo)) return TDI_BUFFER_TOO_SMALL; fStatus = CopyToNdisSafe(Buffer, &Buffer, (uchar *) & ICMPInStats, sizeof(ICMPStats), &Offset); if (fStatus == TRUE) { fStatus = CopyToNdisSafe(Buffer, NULL, (uchar *) & ICMPOutStats, sizeof(ICMPStats), &Offset); if (fStatus == TRUE) { *Size = sizeof(ICMPSNMPInfo); return TDI_SUCCESS; } } return (TDI_NO_RESOURCES); } // It's not ICMP. We need to figure out what it is, and take the // appropriate action. switch (ID->toi_id) { case IP_MIB_STATS_ID: if (BufferSize < sizeof(IPSNMPInfo)) return TDI_BUFFER_TOO_SMALL; IPSInfo.ipsi_numif = NumIF; IPSInfo.ipsi_numaddr = NumActiveNTE; IPSInfo.ipsi_defaultttl = DefaultTTL; IPSInfo.ipsi_forwarding = ForwardPackets ? IP_FORWARDING : IP_NOT_FORWARDING; #if FFP_SUPPORT // // Tweak SNMP information to include information from FFP'ed packets // // Keep a copy of the prev stats for use RtlCopyMemory(&GlobalStatsInfoPrev, &GlobalStatsInfoCurr, sizeof(FFPDriverStats)); // Get the stats by querying the driver IPStatsFromFFPCaches(&GlobalStatsInfoCurr); // These counts missed packets fast fwded from last time a query was made IPPerCpuStats[0].ics_inreceives += GlobalStatsInfoCurr.PacketsForwarded - GlobalStatsInfoPrev.PacketsForwarded; IPSInfo.ipsi_forwdatagrams += GlobalStatsInfoCurr.PacketsForwarded - GlobalStatsInfoPrev.PacketsForwarded; // These counts missed all packets dropped from last time a query was made IPPerCpuStats[0].ics_inreceives += GlobalStatsInfoCurr.PacketsDiscarded - GlobalStatsInfoPrev.PacketsDiscarded; IPSInfo.ipsi_outdiscards += GlobalStatsInfoCurr.PacketsDiscarded - GlobalStatsInfoPrev.PacketsDiscarded; #endif // if FFP_SUPPORT #if !MILLEN IPSGetTotalCounts(&SumCpuStats); IPSInfo.ipsi_inreceives = SumCpuStats.ics_inreceives; IPSInfo.ipsi_indelivers = SumCpuStats.ics_indelivers; #endif fStatus = CopyToNdisSafe(Buffer, NULL, (uchar *) & IPSInfo, sizeof(IPSNMPInfo), &Offset); if (fStatus == TRUE) { BytesCopied = sizeof(IPSNMPInfo); Status = TDI_SUCCESS; } else { Status = TDI_NO_RESOURCES; } break; case IP_MIB_ADDRTABLE_ENTRY_ID: { PINFO_LIST PrimaryList, NonDynamicList, DynamicList, UniList; PINFO_LIST LastPrimaryEle, LastNonDynamicEle, LastDynamicEle, LastUniEle; PINFO_LIST SavedTempInfo = NULL; PINFO_LIST tempInfo; PINFO_LIST FinalList, LastFinalListEle; PINFO_LIST CurrentNTEInfo; // He wants to read the address table. Figure out where we're // starting from, and if it's valid begin copying from there. NTEContext = *(ushort *) Context; // Build 3 lists: Primary, nondynamic nonprimary and dynamic PrimaryList = NULL; NonDynamicList = NULL; DynamicList = NULL; UniList = NULL; LastPrimaryEle = NULL; LastNonDynamicEle = NULL; LastDynamicEle = NULL; LastUniEle = NULL; for (i = 0; i < NET_TABLE_SIZE; i++) { for (CurrentNTE = NewNetTableList[i]; CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) { if ((CurrentNTE->nte_flags & NTE_VALID) && CurrentNTE->nte_if->if_flags & IF_FLAGS_UNI) { // allocate the block to store the info tempInfo = CTEAllocMemN(sizeof(INFO_LIST), '1ICT'); if (!tempInfo) { // free all the lists FreeInfoList(PrimaryList); FreeInfoList(NonDynamicList); FreeInfoList(DynamicList); FreeInfoList(UniList); return TDI_NO_RESOURCES; } if (UniList == NULL) { // this is the last element in this list LastUniEle = tempInfo; } tempInfo->info_nte = CurrentNTE; tempInfo->info_next = UniList; UniList = tempInfo; } else if (CurrentNTE->nte_flags & NTE_PRIMARY) { // allocate the block to store the info tempInfo = CTEAllocMemN(sizeof(INFO_LIST), '1ICT'); if (!tempInfo) { // free all the lists FreeInfoList(PrimaryList); FreeInfoList(NonDynamicList); FreeInfoList(DynamicList); FreeInfoList(UniList); return TDI_NO_RESOURCES; } if (PrimaryList == NULL) { // this is the last element in this list LastPrimaryEle = tempInfo; } tempInfo->info_nte = CurrentNTE; tempInfo->info_next = PrimaryList; PrimaryList = tempInfo; } else if (CurrentNTE->nte_flags & NTE_DYNAMIC) { // allocate the block to store the info tempInfo = CTEAllocMemN(sizeof(INFO_LIST), '1ICT'); if (!tempInfo) { // free all the lists FreeInfoList(PrimaryList); FreeInfoList(NonDynamicList); FreeInfoList(DynamicList); FreeInfoList(UniList); return TDI_NO_RESOURCES; } if (DynamicList == NULL) { // this is the last element in this list LastDynamicEle = tempInfo; } tempInfo->info_nte = CurrentNTE; tempInfo->info_next = DynamicList; DynamicList = tempInfo; } else { INFO_LIST** nextInfo; // Non primary non Dynamic list // allocate the block to store the info tempInfo = CTEAllocMemN(sizeof(INFO_LIST), '1ICT'); if (!tempInfo) { // free all the lists FreeInfoList(PrimaryList); FreeInfoList(NonDynamicList); FreeInfoList(DynamicList); FreeInfoList(UniList); return TDI_NO_RESOURCES; } // Even though we are reading from a hash-table, // we need to preserve the ordering of entries // as given on the entries' interfaces' 'if_ntelist'. // Attempt to find the entry for this NTE's predecessor // and, if found, place this entry before that. // This builds the list in reverse order, and ensures // that an entry whose predecessor is not on the list // will appear last. for (nextInfo = &NonDynamicList; (*nextInfo) && (*nextInfo)->info_nte->nte_ifnext != CurrentNTE; nextInfo = &(*nextInfo)->info_next) { } tempInfo->info_nte = CurrentNTE; tempInfo->info_next = *nextInfo; *nextInfo = tempInfo; if (!tempInfo->info_next) LastNonDynamicEle = tempInfo; } if (NTEContext != 0) { if (CurrentNTE->nte_context == NTEContext) { SavedTempInfo = tempInfo; } } } // for (CurrentNTE ... } // for (i= ... // at this point we have 4 lists and we have to merge 4 lists // order should be Uni -> Dynamic -> NonDynamic -> Primary FinalList = NULL; LastFinalListEle = NULL; if (UniList) { if (FinalList == NULL) { FinalList = UniList; LastFinalListEle = LastUniEle; } else { LastFinalListEle->info_next = UniList; LastFinalListEle = LastUniEle; } } if (DynamicList) { if (FinalList == NULL) { FinalList = DynamicList; LastFinalListEle = LastDynamicEle; } else { LastFinalListEle->info_next = DynamicList; LastFinalListEle = LastDynamicEle; } } if (NonDynamicList) { if (FinalList == NULL) { FinalList = NonDynamicList; LastFinalListEle = LastNonDynamicEle; } else { LastFinalListEle->info_next = NonDynamicList; LastFinalListEle = LastNonDynamicEle; } } if (PrimaryList) { if (FinalList == NULL) { FinalList = PrimaryList; LastFinalListEle = LastPrimaryEle; } else { LastFinalListEle->info_next = PrimaryList; LastFinalListEle = LastPrimaryEle; } } #if MILLEN #if DBG if (DBG_INFO && DBG_VERBOSE && DBG_QUERYINFO) { DEBUGMSG(1, (DTEXT("IP_MIB_ADDRTABLE_ENTRY_ID: List before reverse:\n"))); CurrentNTEInfo = FinalList; while (CurrentNTEInfo) { DEBUGMSG(1, (DTEXT(" InfoList: %x NTE\n"), CurrentNTEInfo, CurrentNTEInfo->info_nte)); CurrentNTEInfo = CurrentNTEInfo->info_next; } } #endif // Now guess what Win9X requires us to...reverse the list. It // expects that the primary is at the start of the list. { PINFO_LIST pCurrInfo, pPrevInfo, pNextInfo; pCurrInfo = FinalList; pPrevInfo = NULL; // Exchange final pointers. FinalList = LastFinalListEle; LastFinalListEle = pCurrInfo; while (pCurrInfo) { pNextInfo = pCurrInfo->info_next; pCurrInfo->info_next = pPrevInfo; pPrevInfo = pCurrInfo; pCurrInfo = pNextInfo; } } #if DBG if (DBG_INFO && DBG_VERBOSE && DBG_QUERYINFO) { DEBUGMSG(1, (DTEXT("IP_MIB_ADDRTABLE_ENTRY_ID: List after reverse:\n"))); CurrentNTEInfo = FinalList; while (CurrentNTEInfo) { DEBUGMSG(1, (DTEXT(" InfoList: %x NTE\n"), CurrentNTEInfo, CurrentNTEInfo->info_nte)); CurrentNTEInfo = CurrentNTEInfo->info_next; } } #endif #endif // MILLEN // we have at least loopback NTE ASSERT(FinalList != NULL); // At this point we have the whole list and also if the user specified NTEContext // we have the pointer saved in SavedTempInfo if (SavedTempInfo) { CurrentNTEInfo = SavedTempInfo; } else { CurrentNTEInfo = FinalList; } AddrEntry = (IPAddrEntry *) InfoBuff; fStatus = TRUE; for (; CurrentNTEInfo != NULL; CurrentNTEInfo = CurrentNTEInfo->info_next) { // NetTableEntry *CurrentNTE = CurrentNTEInfo->info_nte; CurrentNTE = CurrentNTEInfo->info_nte; if (CurrentNTE->nte_flags & NTE_ACTIVE) { if ((int)(BufferSize - BytesCopied) >= (int)sizeof(IPAddrEntry)) { // We have room to copy it. Build the entry, and copy // it. if (CurrentNTE->nte_flags & NTE_VALID) { AddrEntry->iae_addr = CurrentNTE->nte_addr; AddrEntry->iae_mask = CurrentNTE->nte_mask; } else { AddrEntry->iae_addr = NULL_IP_ADDR; AddrEntry->iae_mask = NULL_IP_ADDR; } if (!(CurrentNTE->nte_flags & NTE_IF_DELETING)) { AddrEntry->iae_index = CurrentNTE->nte_if->if_index; AddrEntry->iae_bcastaddr = *(int *)&(CurrentNTE->nte_if->if_bcast) & 1; } else { AddrEntry->iae_index = INVALID_IF_INDEX; AddrEntry->iae_bcastaddr = 0; } AddrEntry->iae_reasmsize = 0xffff; AddrEntry->iae_context = CurrentNTE->nte_context; // LSB will have primary bit set if this is PRIMARY NTE ASSERT((NTE_PRIMARY >> 2) == MIB_IPADDR_PRIMARY); AddrEntry->iae_pad = CurrentNTE->nte_flags >> 2; fStatus = CopyToNdisSafe(Buffer, &Buffer, (uchar *) AddrEntry, sizeof(IPAddrEntry), &Offset); if (fStatus == FALSE) { break; } BytesCopied += sizeof(IPAddrEntry); } else break; } } if (fStatus == FALSE) { Status = TDI_NO_RESOURCES; } else if (CurrentNTEInfo == NULL) { Status = TDI_SUCCESS; } else { Status = TDI_BUFFER_OVERFLOW; **(ushort **) & Context = CurrentNTE->nte_context; } // free the list FreeInfoList(FinalList); break; } case IP_MIB_RTTABLE_ENTRY_ID: // Make sure we have a valid context. CTEGetLock(&RouteTableLock.Lock, &Handle); DataLeft = RTValidateContext(Context, &Valid); // If the context is valid, we'll continue trying to read. if (!Valid) { CTEFreeLock(&RouteTableLock.Lock, Handle); return TDI_INVALID_PARAMETER; } fStatus = TRUE; while (DataLeft) { // The invariant here is that there is data in the table to // read. We may or may not have room for it. So DataLeft // is TRUE, and BufferSize - BytesCopied is the room left // in the buffer. if ((int)(BufferSize - BytesCopied) >= (int)sizeof(IPRouteEntry)) { DataLeft = RTReadNext(Context, InfoBuff); BytesCopied += sizeof(IPRouteEntry); fStatus = CopyToNdisSafe(Buffer, &Buffer, InfoBuff, sizeof(IPRouteEntry), &Offset); if (fStatus == FALSE) { break; } } else break; } CTEFreeLock(&RouteTableLock.Lock, Handle); if (fStatus == FALSE) { Status = TDI_NO_RESOURCES; } else { Status = (!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW); } break; case IP_MIB_SINGLE_RT_ENTRY_ID: { CTEGetLock(&RouteTableLock.Lock, &Handle); if ((int)(BufferSize >= (int)sizeof(IPRouteEntry))) { Status = RTRead(Context, InfoBuff); fStatus = CopyToNdisSafe(Buffer, &Buffer, InfoBuff, sizeof(IPRouteEntry), &Offset); if (fStatus == FALSE) { Status = TDI_NO_RESOURCES; } else { //Status = TDI_SUCCESS; BytesCopied = sizeof(IPRouteEntry); } } else { Status = TDI_BUFFER_OVERFLOW; } CTEFreeLock(&RouteTableLock.Lock, Handle); } break; case IP_INTFC_INFO_ID: IFAddr = *(IPAddr *) Context; // Loop through the NTE table, looking for a match. NetTableList = NewNetTableList[NET_TABLE_HASH(IFAddr)]; for (CurrentNTE = NetTableList; CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) { if ((CurrentNTE->nte_flags & NTE_VALID) && IP_ADDR_EQUAL(CurrentNTE->nte_addr, IFAddr)) break; } if (CurrentNTE == NULL) { Status = TDI_INVALID_PARAMETER; break; } if (BufferSize < offsetof(IPInterfaceInfo, iii_addr)) { Status = TDI_BUFFER_TOO_SMALL; break; } // We have the NTE. Get the interface, fill in a structure, // and we're done. LowerIF = CurrentNTE->nte_if; IIIPtr = (IPInterfaceInfo *) InfoBuff; IIIPtr->iii_flags = 0; if (LowerIF->if_flags & IF_FLAGS_P2P) { IIIPtr->iii_flags |= IP_INTFC_FLAG_P2P; } if (LowerIF->if_flags & IF_FLAGS_P2MP) { IIIPtr->iii_flags |= IP_INTFC_FLAG_P2MP; } if (LowerIF->if_flags & IF_FLAGS_UNI) { IIIPtr->iii_flags |= IP_INTFC_FLAG_UNIDIRECTIONAL; } IIIPtr->iii_mtu = LowerIF->if_mtu; IIIPtr->iii_speed = LowerIF->if_speed; IIIPtr->iii_addrlength = LowerIF->if_addrlen; BytesCopied = offsetof(IPInterfaceInfo, iii_addr); if (BufferSize >= (offsetof(IPInterfaceInfo, iii_addr) + LowerIF->if_addrlen)) { Status = TDI_NO_RESOURCES; fStatus = CopyToNdisSafe(Buffer, &Buffer, InfoBuff, offsetof(IPInterfaceInfo, iii_addr), &Offset); if (fStatus == TRUE) { if (LowerIF->if_addr) { fStatus = CopyToNdisSafe(Buffer, NULL, LowerIF->if_addr, LowerIF->if_addrlen, &Offset); if (fStatus == TRUE) { Status = TDI_SUCCESS; BytesCopied += LowerIF->if_addrlen; } } else { Status = TDI_SUCCESS; } } } else { Status = TDI_BUFFER_TOO_SMALL; } break; case IP_GET_BEST_SOURCE: { IPAddr Dest = * (IPAddr *) Context; IPAddr Source; RouteCacheEntry *RCE; ushort MSS; uchar Type; IPOptInfo OptInfo; if (BufferSize < sizeof Source) { Status = TDI_BUFFER_TOO_SMALL; break; } IPInitOptions(&OptInfo); Source = OpenRCE(Dest, NULL_IP_ADDR, &RCE, &Type, &MSS, &OptInfo); if (!IP_ADDR_EQUAL(Source, NULL_IP_ADDR)) CloseRCE(RCE); fStatus = CopyToNdisSafe(Buffer, &Buffer, (uchar *)&Source, sizeof Source, &Offset); if (fStatus == FALSE) { Status = TDI_NO_RESOURCES; } else { Status = TDI_SUCCESS; BytesCopied = sizeof Source; } break; } default: return TDI_INVALID_PARAMETER; break; } *Size = BytesCopied; return Status; } //* IPSetNdisRequest - IP set ndis request handler. // // Called by the upper layer when it wants to set the general packet filter for // the corr. arp interface // // Input: IPAddr - Addr of addrobject to set on // NDIS_OID - Packet Filter // On - Set_if, clear_if or clear_card // IfIndex - IfIndex if IPAddr not given // // Returns: Matched if index or 0 if failure // ulong IPSetNdisRequest(IPAddr Addr, NDIS_OID OID, uint On, uint IfIndex) { Interface *IF = NULL; NetTableEntry *NTE; int Status; uint i; CTELockHandle Handle; uint Index; // set the interface to promiscuous mcast mode // scan s.t. match numbered interface with Addr or unnumbered interface // with IfIndex // can optimize it by taking special case for unnumbered interface CTEGetLock(&RouteTableLock.Lock, &Handle); for (i = 0; i < NET_TABLE_SIZE; i++) { NetTableEntry *NetTableList = NewNetTableList[i]; for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { if (NTE != LoopNTE && (NTE->nte_flags & NTE_VALID) && (IP_ADDR_EQUAL(NTE->nte_addr, Addr) || NTE->nte_if->if_index == IfIndex)) { // Found one. Save it and break out. IF = NTE->nte_if; break; } } if (IF) { Index = IF->if_index; break; } } if (IF) { if (!IF->if_setndisrequest) { CTEFreeLock(&RouteTableLock.Lock, Handle); return 0; } if (On != CLEAR_CARD) { // just clear the option on the card IF->if_promiscuousmode = (uchar)On; } LOCKED_REFERENCE_IF(IF); CTEFreeLock(&RouteTableLock.Lock, Handle); Status = (*(IF->if_setndisrequest)) (IF->if_lcontext, OID, On); DerefIF(IF); if (Status != NDIS_STATUS_SUCCESS) { return 0; } } else { CTEFreeLock(&RouteTableLock.Lock, Handle); return 0; } return Index; } //* IPAbsorbRtrAlert - IP absorb rtr alert packet handler. // // Called by the upper layer when it wants to set the general packet filter for // the corr. arp interface // // Input: IPAddr - Addr of addrobject to set on // Protocol - if 0 turn of the option // IfIndex - IfIndex if IPAddr not given // // Returns: Matched if index or 0 if failure // ulong IPAbsorbRtrAlert(IPAddr Addr, uchar Protocol, uint IfIndex) { Interface *IF = NULL; NetTableEntry *NTE; int Status; uint i; CTELockHandle Handle; uint Index; // can optimize it by taking special case for unnumbered interface CTEGetLock(&RouteTableLock.Lock, &Handle); for (i = 0; i < NET_TABLE_SIZE; i++) { NetTableEntry *NetTableList = NewNetTableList[i]; for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { if (NTE != LoopNTE && (NTE->nte_flags & NTE_VALID) && (IP_ADDR_EQUAL(NTE->nte_addr, Addr) || NTE->nte_if->if_index == IfIndex)) { // Found one. Save it and break out. IF = NTE->nte_if; break; } } if (IF) { Index = IF->if_index; break; } } if (IF) { // we are keeping this property per interface so if there are 2 NTEs // on that interface its // set/unset on the interface // will decide later whether want to keep it per NTE also. IF->if_absorbfwdpkts = Protocol; CTEFreeLock(&RouteTableLock.Lock, Handle); return Index; } CTEFreeLock(&RouteTableLock.Lock, Handle); return 0; } NTSTATUS SetIFPromiscuous(ULONG Index, UCHAR Type, UCHAR Add) { Interface *pIf; BOOLEAN bFound = FALSE; UINT On; CTELockHandle Handle; CTEGetLock(&RouteTableLock.Lock, &Handle); // // Walk the interface to find the one with the given index // for (pIf = IFList; pIf != NULL; pIf = pIf->if_next) { if ((pIf->if_refcount != 0) && (pIf->if_index == Index)) { bFound = TRUE; break; } } if (!bFound) { CTEFreeLock(&RouteTableLock.Lock, Handle); return STATUS_OBJECT_NAME_NOT_FOUND; } else { LOCKED_REFERENCE_IF(pIf); CTEFreeLock(&RouteTableLock.Lock, Handle); } if (pIf->if_setndisrequest == NULL) { DerefIF(pIf); return STATUS_NOT_SUPPORTED; } if (Add == 0) { On = 0; } else { if (Add == 1) { On = 1; } else { DerefIF(pIf); return STATUS_INVALID_PARAMETER; } } if (Type == PROMISCUOUS_MCAST) { NTSTATUS status; status = (*(pIf->if_setndisrequest)) (pIf->if_lcontext, NDIS_PACKET_TYPE_ALL_MULTICAST, On); DerefIF(pIf); return status; } if (Type == PROMISCUOUS_BCAST) { NTSTATUS status; status = (*(pIf->if_setndisrequest)) (pIf->if_lcontext, NDIS_PACKET_TYPE_PROMISCUOUS, On); DerefIF(pIf); return status; } DerefIF(pIf); return STATUS_INVALID_PARAMETER; } //* IPSetInfo - IP set information handler. // // Called by the upper layer when it wants to set an object, which could // be a route table entry, an ARP table entry, or something else. // // Input: ID - Pointer to ID structure. // Buffer - Pointer to buffer containing element to set.. // Size - Pointer to size in bytes of buffer. // // Returns: TDI_STATUS of attempt to read information. // long IPSetInfo(TDIObjectID * ID, void *Buffer, uint Size) { uint Entity; uint Instance; Interface *LowerIF; Interface *OutIF; uint MTU; IPRouteEntry *IRE; NetTableEntry *OutNTE, *LocalNTE; IP_STATUS Status; IPAddr FirstHop, Dest, NextHop; uint i; CTELockHandle TableHandle; uint Flags; uchar Dtype; DEBUGMSG(DBG_TRACE && DBG_SETINFO, (DTEXT("+IPSetInfo(%x, %x, %d)\n"), ID, Buffer, Size)); Entity = ID->toi_entity.tei_entity; Instance = ID->toi_entity.tei_instance; // If it's not for us, pass it down. if (Entity != CL_NL_ENTITY) { // We need to pass this down to the lower layer. Loop through until // we find one that takes it. If noone does, error out. CTEGetLock(&RouteTableLock.Lock, &TableHandle); LowerIF = IFList; while (LowerIF) { if (LowerIF->if_refcount == 0) { // this interface is about to get deleted // fail the request break; } LOCKED_REFERENCE_IF(LowerIF); CTEFreeLock(&RouteTableLock.Lock, TableHandle); // we have freed the routetablelock here // but since we have a refcount on LowerIF, LowerIF can't go away Status = (*LowerIF->if_setinfo) (LowerIF->if_lcontext, ID, Buffer, Size); if (Status != TDI_INVALID_REQUEST) { DEBUGMSG(DBG_ERROR && DBG_SETINFO, (DTEXT("IPSetInfo: if_setinfo failure %x\n"), Status)); DerefIF(LowerIF); return Status; } CTEGetLock(&RouteTableLock.Lock, &TableHandle); LockedDerefIF(LowerIF); // LowerIF->if_next can't be freed at this point. LowerIF = LowerIF->if_next; } CTEFreeLock(&RouteTableLock.Lock, TableHandle); // If we get here, noone took it. Return an error. return TDI_INVALID_REQUEST; } if (Instance != IPInstance) return TDI_INVALID_REQUEST; // We're identified as the entity. Make sure the ID is correct. Flags = RT_EXCLUDE_LOCAL; if (ID->toi_id == IP_MIB_RTTABLE_ENTRY_ID_EX) { Flags |= RT_NO_NOTIFY; ID->toi_id = IP_MIB_RTTABLE_ENTRY_ID; } if (ID->toi_id == IP_MIB_RTTABLE_ENTRY_ID) { NetTableEntry *TempNTE; DEBUGMSG(DBG_INFO && DBG_SETINFO, (DTEXT("IPSetInfo: IP_MIB_RTTABLE_ENTRY_ID - set route table entry.\n"))); // This is an attempt to set a route table entry. Make sure the // size if correct. if (Size < sizeof(IPRouteEntry)) { DEBUGMSG(DBG_ERROR, (DTEXT("IPSetInfo RTTABLE: Buffer too small %d (IPRouteEntry = %d)\n"), Size, sizeof(IPRouteEntry))); return TDI_INVALID_PARAMETER; } IRE = (IPRouteEntry *) Buffer; OutNTE = NULL; LocalNTE = NULL; Dest = IRE->ire_dest; NextHop = IRE->ire_nexthop; // Make sure that the nexthop is sensible. We don't allow nexthops // to be broadcast or invalid or loopback addresses. if (IP_LOOPBACK(NextHop) || CLASSD_ADDR(NextHop) || CLASSE_ADDR(NextHop)) { DEBUGMSG(DBG_ERROR, (DTEXT("IPSetInfo RTTABLE: Invalid next hop %x\n"), NextHop)); return TDI_INVALID_PARAMETER; } // Also make sure that the destination we're routing to is sensible. // Don't allow routes to be added to E or loopback addresses if (IP_LOOPBACK(Dest) || CLASSE_ADDR(Dest)) return TDI_INVALID_PARAMETER; if (IRE->ire_index == LoopIndex) { DEBUGMSG(DBG_ERROR, (DTEXT("IPSetInfo RTTABLE: index == LoopIndex!! Invalid!\n"))); return TDI_INVALID_PARAMETER; } if (IRE->ire_index != INVALID_IF_INDEX) { // First thing to do is to find the outgoing NTE for specified // interface, and also make sure that it matches the destination // if the destination is one of my addresses. for (i = 0; i < NET_TABLE_SIZE; i++) { NetTableEntry *NetTableList = NewNetTableList[i]; for (TempNTE = NetTableList; TempNTE != NULL; TempNTE = TempNTE->nte_next) { if ((OutNTE == NULL) && (TempNTE->nte_flags & NTE_VALID) && (IRE->ire_index == TempNTE->nte_if->if_index)) OutNTE = TempNTE; if (!IP_ADDR_EQUAL(NextHop, NULL_IP_ADDR) && IP_ADDR_EQUAL(NextHop, TempNTE->nte_addr) && (TempNTE->nte_flags & NTE_VALID)) LocalNTE = TempNTE; // Don't let a route be set through a broadcast address. if (IsBCastOnNTE(NextHop, TempNTE) != DEST_LOCAL) { DEBUGMSG(DBG_ERROR, (DTEXT("IPSetInfo RTTABLE: Bcast address. Invalid NextHop!\n"))); return TDI_INVALID_PARAMETER; } // Don't let a route to a broadcast address be added or deleted. Dtype = IsBCastOnNTE(Dest, TempNTE); if ((Dtype != DEST_LOCAL) && (Dtype != DEST_MCAST)) { DEBUGMSG(DBG_ERROR, (DTEXT("IPSetInfo RTTABLE: Bcast address. Invalid Dest!\n"))); return TDI_INVALID_PARAMETER; } } } // At this point OutNTE points to the outgoing NTE, and LocalNTE // points to the NTE for the local address, if this is a direct route. // Make sure they point to the same interface, and that the type is // reasonable. if (OutNTE == NULL) return TDI_INVALID_PARAMETER; if (LocalNTE != NULL) { // He's routing straight out a local interface. The interface for // the local address must match the interface passed in, and the // type must be DIRECT (if we're adding) or INVALID (if we're // deleting). // LocalNTE is valid at this point if (LocalNTE->nte_if->if_index != IRE->ire_index) return TDI_INVALID_PARAMETER; if (IRE->ire_type != IRE_TYPE_DIRECT && IRE->ire_type != IRE_TYPE_INVALID) return TDI_INVALID_PARAMETER; OutNTE = LocalNTE; } // Figure out what the first hop should be. If he's routing straight // through a local interface, or the next hop is equal to the // destination, then the first hop is IPADDR_LOCAL. Otherwise it's the // address of the gateway. if ((LocalNTE != NULL) || IP_ADDR_EQUAL(NextHop, NULL_IP_ADDR)) FirstHop = IPADDR_LOCAL; else if (IP_ADDR_EQUAL(Dest, NextHop)) FirstHop = IPADDR_LOCAL; else FirstHop = NextHop; MTU = OutNTE->nte_mss; // Take RouteTableLock CTEGetLock(&RouteTableLock.Lock, &TableHandle); if ((OutNTE->nte_flags & NTE_VALID) && OutNTE->nte_if->if_refcount) { // ref the IF OutIF = OutNTE->nte_if; if (IP_ADDR_EQUAL(NextHop, NULL_IP_ADDR)) { if (!(OutIF->if_flags & IF_FLAGS_P2P)) { CTEFreeLock(&RouteTableLock.Lock, TableHandle); return TDI_INVALID_PARAMETER; } } LOCKED_REFERENCE_IF(OutIF); CTEFreeLock(&RouteTableLock.Lock, TableHandle); } else { CTEFreeLock(&RouteTableLock.Lock, TableHandle); return TDI_INVALID_PARAMETER; } OutIF = OutNTE->nte_if; } else { OutIF = (Interface *) & DummyInterface; MTU = DummyInterface.ri_if.if_mtu - sizeof(IPHeader); if (IP_ADDR_EQUAL(Dest, NextHop)) FirstHop = IPADDR_LOCAL; else FirstHop = NextHop; } // We've done the validation. See if he's adding or deleting a route. if (IRE->ire_type != IRE_TYPE_INVALID) { // He's adding a route. uint AType = ATYPE_OVERRIDE; DEBUGMSG(DBG_INFO && DBG_SETINFO, (DTEXT("IPSetInfo RTTABLE: Calling AddRoute addr %x mask %x\n"), Dest, IRE->ire_mask)); Status = AddRoute(Dest, IRE->ire_mask, FirstHop, OutIF, MTU, IRE->ire_metric1, IRE->ire_proto, AType, IRE->ire_context, Flags); DEBUGMSG(Status != IP_SUCCESS && DBG_ERROR && DBG_SETINFO, (DTEXT("IPSetInfo: AddRoute failure %x\n"), Status)); } else { DEBUGMSG(DBG_INFO && DBG_SETINFO, (DTEXT("IPSetInfo RTTABLE: Calling DeleteRoute addr %x mask %x\n"), Dest, IRE->ire_mask)); // He's deleting a route. Status = DeleteRoute(Dest, IRE->ire_mask, FirstHop, OutIF, Flags); DEBUGMSG(Status != IP_SUCCESS && DBG_ERROR && DBG_SETINFO, (DTEXT("IPSetInfo: DeleteRoute failure %x\n"), Status)); } if (IRE->ire_index != INVALID_IF_INDEX) { ASSERT(OutIF != (Interface *) & DummyInterface); DerefIF(OutIF); } if (Status == IP_SUCCESS) return TDI_SUCCESS; else if (Status == IP_NO_RESOURCES) return TDI_NO_RESOURCES; else return TDI_INVALID_PARAMETER; } else { if (ID->toi_id == IP_MIB_STATS_ID) { IPSNMPInfo *Info = (IPSNMPInfo *) Buffer; // Setting information about TTL and/or routing. if (Info->ipsi_defaultttl > 255 || (!RouterConfigured && Info->ipsi_forwarding == IP_FORWARDING)) { return TDI_INVALID_PARAMETER; } DefaultTTL = Info->ipsi_defaultttl; ForwardPackets = Info->ipsi_forwarding == IP_FORWARDING ? TRUE : FALSE; return TDI_SUCCESS; } return TDI_INVALID_PARAMETER; } } #pragma BEGIN_INIT //* IPGetEList - Get the entity list. // // Called at init time to get an entity list. We fill our stuff in, and // then call the interfaces below us to allow them to do the same. // // Input: EntityList - Pointer to entity list to be filled in. // Count - Pointer to number of entries in the list. // // Returns Status of attempt to get the info. // long IPGetEList(TDIEntityID * EList, uint * Count) { uint ECount; uint MyIPBase; uint MyERBase; int Status; uint i; Interface *LowerIF; TDIEntityID *EntityList; TDIEntityID *IPEntity, *EREntity; CTELockHandle TableHandle; EntityList = EList; // Walk down the list, looking for existing CL_NL or ER entities, and // adjust our base instance accordingly. // if we are already on the list then do nothing. // if we are going away, mark our entry invalid. MyIPBase = 0; MyERBase = 0; IPEntity = NULL; EREntity = NULL; for (i = 0; i < *Count; i++, EntityList++) { if (EntityList->tei_entity == CL_NL_ENTITY && EntityList->tei_entity != INVALID_ENTITY_INSTANCE) { // if we are already on the list remember our entity item // o/w find an instance # for us. if (EntityList->tei_instance == IPInstance) { IPEntity = EntityList; } else { MyIPBase = MAX(MyIPBase, EntityList->tei_instance + 1); } } else { if (EntityList->tei_entity == ER_ENTITY && EntityList->tei_entity != INVALID_ENTITY_INSTANCE) // if we are already on the list remember our entity item // o/w find an instance # for us. if (EntityList->tei_instance == ICMPInstance) { EREntity = EntityList; } else { MyERBase = MAX(MyERBase, EntityList->tei_instance + 1); } } if (IPEntity && EREntity) { break; } } if (!IPEntity) { // we are not on the list. // insert ourself iff we are not going away. // make sure we have the room for it. if (*Count >= MAX_TDI_ENTITIES) { return TDI_REQ_ABORTED; } IPInstance = MyIPBase; IPEntity = &EList[*Count]; IPEntity->tei_entity = CL_NL_ENTITY; IPEntity->tei_instance = MyIPBase; (*Count)++; } if (!EREntity) { // we are not on the list. // insert ourself iff we are not going away. // make sure we have the room for it. if (*Count >= MAX_TDI_ENTITIES) { return TDI_REQ_ABORTED; } ICMPInstance = MyERBase; EREntity = &EList[*Count]; EREntity->tei_entity = ER_ENTITY; EREntity->tei_instance = MyERBase; (*Count)++; } // Loop through the interfaces, querying each of them. CTEGetLock(&RouteTableLock.Lock, &TableHandle); LowerIF = IFList; while (LowerIF) { if (LowerIF->if_refcount == 0) { LowerIF = LowerIF->if_next; continue; } LOCKED_REFERENCE_IF(LowerIF); CTEFreeLock(&RouteTableLock.Lock, TableHandle); Status = (*LowerIF->if_getelist) (LowerIF->if_lcontext, EList, Count); if (!Status) { DerefIF(LowerIF); return TDI_BUFFER_TOO_SMALL; } CTEGetLock(&RouteTableLock.Lock, &TableHandle); LockedDerefIF(LowerIF); // LowerIF->if_next can't be freed at this point. LowerIF = LowerIF->if_next; } // Finally, cache the entries that are now on the list. // Note that our cache is covered by 'RouteTableLock'. if (!IPEntityList) { IPEntityList = CTEAllocMem(sizeof(TDIEntityID) * MAX_TDI_ENTITIES); } if (IPEntityList) { RtlZeroMemory(IPEntityList, sizeof(IPEntityList)); if (IPEntityCount = *Count) { RtlCopyMemory(IPEntityList, EList, IPEntityCount * sizeof(*EList)); } } CTEFreeLock(&RouteTableLock.Lock, TableHandle); return TDI_SUCCESS; } #pragma END_INIT //* IPWakeupPattern - add or remove IP wakeup pattern. // // Entry: InterfaceConext - ip interface context for which the pattern is to be added/removed // PtrnDesc - Pattern Descriptor // AddPattern - TRUE - add, FALSE - remove // Returns: Nothing. // NTSTATUS IPWakeupPattern(uint InterfaceContext, PNET_PM_WAKEUP_PATTERN_DESC PtrnDesc, BOOLEAN AddPattern) { Interface *IF; CTELockHandle Handle; NTSTATUS status; CTEGetLock(&RouteTableLock.Lock, &Handle); for (IF = IFList; IF != NULL; IF = IF->if_next) { if ((IF->if_refcount != 0) && (IF->if_index == InterfaceContext)) { break; } } if (IF == (Interface *) NULL) { CTEFreeLock(&RouteTableLock.Lock, Handle); return STATUS_INVALID_HANDLE; } else { LOCKED_REFERENCE_IF(IF); CTEFreeLock(&RouteTableLock.Lock, Handle); } if (NULL == IF->if_dowakeupptrn) { DerefIF(IF); return STATUS_NOT_SUPPORTED; } status = (*(IF->if_dowakeupptrn)) (IF->if_lcontext, PtrnDesc, ARP_ETYPE_IP, AddPattern); DerefIF(IF); return status; } NTSTATUS IPGetCapability(uint InterfaceContext, PULONG pBuf, uint cap) { Interface *IF; CTELockHandle Handle; NTSTATUS status; status = STATUS_SUCCESS; CTEGetLock(&RouteTableLock.Lock, &Handle); for (IF = IFList; IF != NULL; IF = IF->if_next) { if ((IF->if_refcount != 0) && (IF->if_index == InterfaceContext)) { break; } } if (IF != (Interface *) NULL) { if (cap == IF_WOL_CAP) { *pBuf = IF->if_pnpcap; } else if (cap == IF_OFFLOAD_CAP) { *pBuf = IF->if_OffloadFlags; } else { status = STATUS_INVALID_PARAMETER; } } else { status = STATUS_INVALID_PARAMETER; } CTEFreeLock(&RouteTableLock.Lock, Handle); return status; } //* IPGetInterfaceFriendlyName - get human-readable name for an interface. // // Called to retrieve the unique descriptive name for an interface. This name // is provided by the interface's ARP module, and is used by IP to identify // the interface in event logs. // // Input: InterfaceContext - IP interface context identifying the interface // friendly name is required // Name - on output, contains the friendly-name. // Size - contains the length of the buffer at 'Name'. // // Returns: TDI_STATUS of query-attempt. long IPGetInterfaceFriendlyName(uint InterfaceContext, PWCHAR Name, uint Size) { PNDIS_BUFFER Buffer; uint BufferSize; CTELockHandle Handle; uint i; TDIObjectID ID; Interface *IF; TDI_STATUS Status; // Attempt to retrieve the interface whose name is required, // and if successful issue a query-info request to get its friendly name. CTEGetLock(&RouteTableLock.Lock, &Handle); for (IF = IFList; IF != NULL; IF = IF->if_next) { if (IF->if_refcount != 0 && IF->if_index == InterfaceContext) { break; } } if (IF != (Interface *) NULL) { // Construct a TDI query to obtain the interface's friendly name. // Unfortunately, this operation is complicated by the fact that // we don't have the exact entity-instance for the lower-layer entity. // Therefore, we go through our whole cache of entity-instances, // until we find one which is acceptable to the lower-layer entity. ID.toi_class = INFO_CLASS_PROTOCOL; ID.toi_type = INFO_TYPE_PROVIDER; ID.toi_id = IF_FRIENDLY_NAME_ID; ID.toi_entity.tei_entity = IF_ENTITY; NdisAllocateBuffer(&Status, &Buffer, BufferPool, Name, Size); if (Status == NDIS_STATUS_SUCCESS) { LOCKED_REFERENCE_IF(IF); for (i = 0; i < IPEntityCount; i++) { if (IPEntityList[i].tei_entity != IF_ENTITY) continue; ID.toi_entity.tei_instance = IPEntityList[i].tei_instance; CTEFreeLock(&RouteTableLock.Lock, Handle); BufferSize = Size; Status = (*IF->if_qinfo)(IF->if_lcontext, &ID, Buffer, &BufferSize, NULL); CTEGetLock(&RouteTableLock.Lock, &Handle); if (Status != TDI_INVALID_REQUEST) break; // We just released the route-table lock in order to query // the lower-layer entity, and that means that the entity-list // may have changed. Handle that case by just making sure // that the entity we just queried is in the same location; // if not, we'll need to find it and continue from there. // If it's gone, give up. if (i < IPEntityCount && IPEntityList[i].tei_instance != ID.toi_entity.tei_instance) { for (i = 0; i < IPEntityCount; i++) { if (IPEntityList[i].tei_instance == ID.toi_entity.tei_instance) { break; } } } } LockedDerefIF(IF); NdisFreeBuffer(Buffer); } else Status = TDI_NO_RESOURCES; } else Status = TDI_INVALID_PARAMETER; CTEFreeLock(&RouteTableLock.Lock, Handle); return Status; } #if MILLEN // // Support for VIP!!! For legacy support in VIP, we need to be able to convert // the index into the if_pnpcontext. This will be exported from tcpip.sys // to be accessed directly by VIP. // //* IPGetPNPCtxt // // Entry: index - ip interface index // PNPCtxt - pointer to pnpcontext // NTSTATUS IPGetPNPCtxt(uint index, uint *PNPCtxt) { Interface *IF; for (IF = IFList; IF != NULL; IF = IF->if_next) { if (IF->if_index == index) { break; } } if ( IF == (Interface *)NULL ) { return STATUS_UNSUCCESSFUL; } *PNPCtxt = (uint)IF->if_pnpcontext; return STATUS_SUCCESS; } //* IPGetPNPCap - add or remove IP wakeup pattern. // // Entry: InterfaceConext - ip interface context for which the wol capability needs to be returned // flags - pointer to capability flags // NTSTATUS IPGetPNPCap(uchar *Context, uint *flags) { Interface *IF; for (IF = IFList; IF != NULL; IF = IF->if_next) { if (IF->if_pnpcontext == Context) { break; } } if ( IF == (Interface *)NULL ) { return STATUS_UNSUCCESSFUL; } *flags = IF->if_pnpcap; return STATUS_SUCCESS; } #endif // MILLEN