diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/tcpip/ip/info.c | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/ip/info.c b/private/ntos/tdi/tcpip/ip/info.c new file mode 100644 index 000000000..a866c0cba --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/info.c @@ -0,0 +1,609 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** INFO.C - Routines for querying and setting IP information. +// +// This file contains the code for dealing with Query/Set information +// calls. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "ipdef.h" +#include "info.h" +#include "tdi.h" +#include "tdiinfo.h" +#include "llinfo.h" +#include "tdistat.h" +#include "iproute.h" +#include "igmp.h" +#include "ipfilter.h" +#include "iprtdef.h" + +extern Interface *IFList; +extern NetTableEntry *NetTableList; +extern uint LoopIndex; // Index of loopback I/F. +extern uint DefaultTTL; +extern uint NumIF; +extern uint NumNTE; +extern RouteInterface DummyInterface; // Dummy interface. + +EXTERNAL_LOCK(RouteTableLock) + +extern uint RTEReadNext(void *Context, void *Buffer); +extern uint RTValidateContext(void *Context, uint *Valid); +extern uint RTReadNext(void *Context, void *Buffer); + +uint IPInstance; +uint ICMPInstance; + +//* CopyToNdis - 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. +// 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: Pointer to next buffer in chain to copy into. +// +PNDIS_BUFFER +CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size, + uint *StartOffset) +{ + uint CopySize; + uchar *DestPtr; + uint DestSize; + uint Offset = *StartOffset; + uchar *VirtualAddress; + uint Length; + + CTEAssert(DestBuf != NULL); + CTEAssert(SrcBuf != NULL); + + NdisQueryBuffer(DestBuf, &VirtualAddress, &Length); + CTEAssert(Length >= Offset); + DestPtr = VirtualAddress + Offset; + DestSize = Length - Offset; + + for (;;) { + CopySize = MIN(Size, DestSize); + CTEMemCopy(DestPtr, SrcBuf, CopySize); + + DestPtr += CopySize; + SrcBuf += CopySize; + + if ((Size -= CopySize) == 0) + break; + + if ((DestSize -= CopySize) == 0) { + DestBuf = NDIS_BUFFER_LINKAGE(DestBuf); + CTEAssert(DestBuf != NULL); + NdisQueryBuffer(DestBuf, &VirtualAddress, &Length); + DestPtr = VirtualAddress; + DestSize = Length; + } + } + + *StartOffset = DestPtr - VirtualAddress; + + return DestBuf; + +} + + +//* 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; + + + 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. + for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) { + Status = (*LowerIF->if_qinfo)(LowerIF->if_lcontext, ID, Buffer, + Size, Context); + if (Status != TDI_INVALID_REQUEST) + return Status; + } + // 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; + (void)CopyToNdis(Buffer, InfoBuff, sizeof(uint), &Offset); + 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; + + Buffer = CopyToNdis(Buffer, (uchar *)&ICMPInStats, sizeof(ICMPStats), + &Offset); + (void)CopyToNdis(Buffer, (uchar *)&ICMPOutStats, sizeof(ICMPStats), + &Offset); + + *Size = sizeof(ICMPSNMPInfo); + return TDI_SUCCESS; + } + + // 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 = NumNTE; + IPSInfo.ipsi_defaultttl = DefaultTTL; + IPSInfo.ipsi_forwarding = ForwardPackets ? IP_FORWARDING : + IP_NOT_FORWARDING; + CopyToNdis(Buffer, (uchar *)&IPSInfo, sizeof(IPSNMPInfo), &Offset); + BytesCopied = sizeof(IPSNMPInfo); + Status = TDI_SUCCESS; + break; + case IP_MIB_ADDRTABLE_ENTRY_ID: + // 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; + CurrentNTE = NetTableList; + + if (NTEContext != 0) { + for (;CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) + if (CurrentNTE->nte_context == NTEContext) + break; + if (CurrentNTE == NULL) + return TDI_INVALID_PARAMETER; + } + + AddrEntry = (IPAddrEntry *)InfoBuff; + for (; CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) { + if ((int)(BufferSize - BytesCopied) >= (int)sizeof(IPAddrEntry)) { + // We have room to copy it. Build the entry, and copy + // it. + if (CurrentNTE->nte_flags & NTE_ACTIVE) { + 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; + } + + AddrEntry->iae_index = CurrentNTE->nte_if->if_index; + AddrEntry->iae_bcastaddr = + *(int *)&(CurrentNTE->nte_if->if_bcast) & 1; + AddrEntry->iae_reasmsize = 0xffff; + AddrEntry->iae_context = CurrentNTE->nte_context; + Buffer = CopyToNdis(Buffer, (uchar *)AddrEntry, + sizeof(IPAddrEntry), &Offset); + BytesCopied += sizeof(IPAddrEntry); + } + } else + break; + } + + if (CurrentNTE == NULL) + Status = TDI_SUCCESS; + else { + Status = TDI_BUFFER_OVERFLOW; + **(ushort **)&Context = CurrentNTE->nte_context; + } + + break; + case IP_MIB_RTTABLE_ENTRY_ID: + // Make sure we have a valid context. + CTEGetLock(&RouteTableLock, &Handle); + DataLeft = RTValidateContext(Context, &Valid); + + // If the context is valid, we'll continue trying to read. + if (!Valid) { + CTEFreeLock(&RouteTableLock, Handle); + return TDI_INVALID_PARAMETER; + } + + 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); + Buffer = CopyToNdis(Buffer, InfoBuff, sizeof(IPRouteEntry), + &Offset); + } else + break; + + } + + CTEFreeLock(&RouteTableLock, Handle); + Status = (!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW); + break; + case IP_INTFC_INFO_ID: + + IFAddr = *(IPAddr *)Context; + // Loop through the NTE table, looking for a match. + 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 = LowerIF->if_flags & IF_FLAGS_P2P ? + IP_INTFC_FLAG_P2P : 0; + 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_SUCCESS; + Buffer = CopyToNdis(Buffer, InfoBuff, + offsetof(IPInterfaceInfo, iii_addr), &Offset); + CopyToNdis(Buffer, LowerIF->if_addr, LowerIF->if_addrlen, + &Offset); + BytesCopied += LowerIF->if_addrlen; + } else { + Status = TDI_BUFFER_TOO_SMALL; + } + break; + + default: + return TDI_INVALID_PARAMETER; + break; + } + + *Size = BytesCopied; + return Status; +} + +//* 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; + + 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. + for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) { + Status = (*LowerIF->if_setinfo)(LowerIF->if_lcontext, ID, Buffer, + Size); + if (Status != TDI_INVALID_REQUEST) + return Status; + } + // 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. + if (ID->toi_id == IP_MIB_RTTABLE_ENTRY_ID) { + NetTableEntry *TempNTE; + + // This is an attempt to set a route table entry. Make sure the + // size if correct. + if (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_ADDR_EQUAL(NextHop, NULL_IP_ADDR) || IP_LOOPBACK(NextHop) || + CLASSD_ADDR(NextHop) || CLASSE_ADDR(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 Class D or E or loopback + // addresses. + if (IP_LOOPBACK(Dest) || CLASSD_ADDR(Dest) || CLASSE_ADDR(Dest)) + return TDI_INVALID_PARAMETER; + + if (IRE->ire_index == LoopIndex) + 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 (TempNTE = NetTableList; TempNTE != NULL; + TempNTE = TempNTE->nte_next) { + if (OutNTE == NULL && IRE->ire_index == TempNTE->nte_if->if_index) + OutNTE = TempNTE; + if (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) + return TDI_INVALID_PARAMETER; + + // Don't let a route to a broadcast address be added or deleted. + if (IsBCastOnNTE(Dest, TempNTE) != DEST_LOCAL) + 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). + 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) + FirstHop = IPADDR_LOCAL; + else + if (IP_ADDR_EQUAL(Dest, NextHop)) + FirstHop = IPADDR_LOCAL; + else + FirstHop = NextHop; + + MTU = OutNTE->nte_mss; + 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. + Status = AddRoute(Dest, IRE->ire_mask, FirstHop, OutIF, + MTU, IRE->ire_metric1, IRE->ire_proto, + ATYPE_OVERRIDE, IRE->ire_context); + + } else { + // He's deleting a route. + Status = DeleteRoute(Dest, IRE->ire_mask, FirstHop, 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; + } + +} +#ifndef CHICAGO +#pragma BEGIN_INIT +#endif + +//* 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; + + ECount = *Count; + EntityList = EList; + + // Walk down the list, looking for existing CL_NL or ER entities, and + // adjust our base instance accordingly. + + MyIPBase = 0; + MyERBase = 0; + for (i = 0; i < ECount; i++, EntityList++) { + if (EntityList->tei_entity == CL_NL_ENTITY) + MyIPBase = MAX(MyIPBase, EntityList->tei_instance + 1); + else + if (EntityList->tei_entity == ER_ENTITY) + MyERBase = MAX(MyERBase, EntityList->tei_instance + 1); + } + + // At this point we've figure out our base instance. Save for later use. + IPInstance = MyIPBase; + ICMPInstance = MyERBase; + + // EntityList points to the start of where we want to begin filling in. + // Make sure we have enough room. We need one for the ICMP instance, + // and one for the CL_NL instance. + + if ((ECount + 2) > MAX_TDI_ENTITIES) + return TDI_REQ_ABORTED; + + // Now fill it in. + EntityList->tei_entity = CL_NL_ENTITY; + EntityList->tei_instance = IPInstance; + EntityList++; + EntityList->tei_entity = ER_ENTITY; + EntityList->tei_instance = ICMPInstance; + *Count += 2; + + // Loop through the interfaces, querying each of them. + for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) { + Status = (*LowerIF->if_getelist)(LowerIF->if_lcontext, EList, Count); + if (!Status) + return TDI_BUFFER_TOO_SMALL; + } + + return TDI_SUCCESS; +} + +#ifndef CHICAGO +#pragma END_INIT +#endif |