From 50ee081131825657e25e731dcf43f02f88eae5fa Mon Sep 17 00:00:00 2001 From: Art Yerkes Date: Fri, 22 Sep 2006 06:36:16 +0000 Subject: [PATCH] ARP query support. Thanks to WaxDragon for encouragement and debugging. svn path=/branches/ros-branch-0_3_1/; revision=24226 --- reactos/dll/win32/iphlpapi/ifenum_reactos.c | 33 +++++- reactos/dll/win32/iphlpapi/iphlpapi_private.h | 5 +- reactos/dll/win32/iphlpapi/ipstats_reactos.c | 100 +++++++++++++++++- reactos/drivers/network/tcpip/include/info.h | 16 +++ .../drivers/network/tcpip/include/neighbor.h | 4 + .../drivers/network/tcpip/include/precomp.h | 2 +- reactos/drivers/network/tcpip/tcpip/iinfo.c | 51 ++++++++- reactos/drivers/network/tcpip/tcpip/info.c | 29 +++++ reactos/include/psdk/tcpioctl.h | 1 + reactos/lib/drivers/ip/network/neighbor.c | 40 +++++++ 10 files changed, 271 insertions(+), 10 deletions(-) diff --git a/reactos/dll/win32/iphlpapi/ifenum_reactos.c b/reactos/dll/win32/iphlpapi/ifenum_reactos.c index 69f7dbd204b..37425705880 100644 --- a/reactos/dll/win32/iphlpapi/ifenum_reactos.c +++ b/reactos/dll/win32/iphlpapi/ifenum_reactos.c @@ -40,7 +40,7 @@ #include "ifenum.h" #include -#define NDEBUG +//#define NDEBUG #include "debug.h" /* Globals */ @@ -124,6 +124,7 @@ NTSTATUS tdiGetSetOfThings( HANDLE tcpFile, DWORD toiType, DWORD toiId, DWORD teiEntity, + DWORD teiInstance, DWORD fixedPart, DWORD entrySize, PVOID *tdiEntitySet, @@ -148,6 +149,7 @@ NTSTATUS tdiGetSetOfThings( HANDLE tcpFile, req.ID.toi_type = toiType; req.ID.toi_id = toiId; req.ID.toi_entity.tei_entity = teiEntity; + req.ID.toi_entity.tei_instance = teiInstance; /* There's a subtle problem here... * If an interface is added at this exact instant, (as if by a PCMCIA @@ -168,7 +170,7 @@ NTSTATUS tdiGetSetOfThings( HANDLE tcpFile, &allocationSizeForEntityArray, NULL ); - if(!status) + if(!NT_SUCCESS(status)) { DPRINT("IOCTL Failed\n"); return STATUS_UNSUCCESSFUL; @@ -285,6 +287,7 @@ NTSTATUS tdiGetEntityIDSet( HANDLE tcpFile, INFO_TYPE_PROVIDER, ENTITY_LIST_ID, GENERIC_ENTITY, + 0, 0, sizeof(TDIEntityID), (PVOID *)entitySet, @@ -303,7 +306,7 @@ NTSTATUS tdiGetEntityIDSet( HANDLE tcpFile, return status; } -static BOOL isInterface( TDIEntityID *if_maybe ) { +BOOL isInterface( TDIEntityID *if_maybe ) { return if_maybe->tei_entity == IF_ENTITY; } @@ -348,6 +351,30 @@ NTSTATUS tdiGetEntityType( HANDLE tcpFile, TDIEntityID *ent, PULONG type ) { return (status ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); } +BOOL hasArp( HANDLE tcpFile, TDIEntityID *arp_maybe ) { + TCP_REQUEST_QUERY_INFORMATION_EX req = TCP_REQUEST_QUERY_INFORMATION_INIT; + NTSTATUS status = STATUS_SUCCESS; + DWORD returnSize, type; + + req.ID.toi_class = INFO_CLASS_GENERIC; + req.ID.toi_type = INFO_TYPE_PROVIDER; + req.ID.toi_id = ENTITY_TYPE_ID; + req.ID.toi_entity.tei_entity = AT_ENTITY; + req.ID.toi_entity.tei_instance = arp_maybe->tei_instance; + + status = DeviceIoControl( tcpFile, + IOCTL_TCP_QUERY_INFORMATION_EX, + &req, + sizeof(req), + &type, + sizeof(type), + &returnSize, + NULL ); + + if( !NT_SUCCESS(status) ) return FALSE; + return type == AT_ENTITY; +} + static NTSTATUS getInterfaceInfoSet( HANDLE tcpFile, IFInfo **infoSet, PDWORD numInterfaces ) { diff --git a/reactos/dll/win32/iphlpapi/iphlpapi_private.h b/reactos/dll/win32/iphlpapi/iphlpapi_private.h index 4d4fb1ed47c..40c4a0bd5d3 100644 --- a/reactos/dll/win32/iphlpapi/iphlpapi_private.h +++ b/reactos/dll/win32/iphlpapi/iphlpapi_private.h @@ -118,7 +118,8 @@ VOID closeTcpFile(HANDLE tcpFile); NTSTATUS tdiGetEntityIDSet( HANDLE tcpFile, TDIEntityID **entitySet, PDWORD numEntities ); NTSTATUS tdiGetSetOfThings( HANDLE tcpFile, DWORD toiClass, DWORD toiType, - DWORD toiId, DWORD teiEntity, DWORD fixedPart, + DWORD toiId, DWORD teiEntity, DWORD teiInstance, + DWORD fixedPart, DWORD entrySize, PVOID *tdiEntitySet, PDWORD numEntries ); VOID tdiFreeThingSet( PVOID things ); @@ -134,6 +135,8 @@ PWCHAR GetNthChildKeyName( HANDLE RegHandle, DWORD n ); void ConsumeChildKeyName( PWCHAR Name ); PWCHAR QueryRegistryValueString( HANDLE RegHandle, PWCHAR ValueName ); void ConsumeRegValueString( PWCHAR NameServer ); +BOOL isInterface( TDIEntityID *if_maybe ); +BOOL hasArp( HANDLE tcpFile, TDIEntityID *arp_maybe ); #include /* This is here until we switch to version 2.5 of the mingw headers */ diff --git a/reactos/dll/win32/iphlpapi/ipstats_reactos.c b/reactos/dll/win32/iphlpapi/ipstats_reactos.c index 4322937fedb..b6b160c4fb7 100644 --- a/reactos/dll/win32/iphlpapi/ipstats_reactos.c +++ b/reactos/dll/win32/iphlpapi/ipstats_reactos.c @@ -183,6 +183,7 @@ NTSTATUS tdiGetRoutesForIpEntity INFO_TYPE_PROVIDER, IP_MIB_ROUTETABLE_ENTRY_ID, ent->tei_entity, + 0, 0, sizeof(IPRouteEntry), (PVOID *)routes, @@ -203,6 +204,7 @@ NTSTATUS tdiGetIpAddrsForIpEntity INFO_TYPE_PROVIDER, IP_MIB_ADDRTABLE_ENTRY_ID, ent->tei_entity, + 0, 0, sizeof(IPAddrEntry), (PVOID *)addrs, @@ -507,12 +509,106 @@ RouteTable *getRouteTable(void) DWORD getNumArpEntries(void) { - return getNumWithOneHeader("/proc/net/arp"); + DWORD numEntities; + TDIEntityID *entitySet = NULL; + HANDLE tcpFile; + int i, totalNumber = 0; + NTSTATUS status; + PMIB_IPNETROW IpArpTable = NULL; + DWORD returnSize; + + TRACE("called.\n"); + + status = openTcpFile( &tcpFile ); + + if( !NT_SUCCESS(status) ) { + TRACE("failure: %08x\n", (int)status ); + return 0; + } + + status = tdiGetEntityIDSet( tcpFile, &entitySet, &numEntities ); + + for( i = 0; i < numEntities; i++ ) { + if( isInterface( &entitySet[i] ) && + hasArp( tcpFile, &entitySet[i] ) ) { + + status = tdiGetSetOfThings( tcpFile, + INFO_CLASS_PROTOCOL, + INFO_TYPE_PROVIDER, + IP_MIB_ARPTABLE_ENTRY_ID, + AT_ENTITY, + entitySet[i].tei_instance, + 0, + sizeof(MIB_IPNETROW), + (PVOID *)&IpArpTable, + &returnSize ); + + if( status == STATUS_SUCCESS ) totalNumber += returnSize; + if( IpArpTable ) tdiFreeThingSet( IpArpTable ); + } + } + + closeTcpFile( tcpFile ); + if( IpArpTable ) tdiFreeThingSet( IpArpTable ); + if( entitySet ) tdiFreeThingSet( entitySet ); + return totalNumber; } PMIB_IPNETTABLE getArpTable(void) { - return 0; + DWORD numEntities, returnSize; + TDIEntityID *entitySet; + HANDLE tcpFile; + int i, row = 0, totalNumber; + NTSTATUS status; + PMIB_IPNETTABLE IpArpTable = NULL; + PMIB_IPNETROW AdapterArpTable = NULL; + + TRACE("called.\n"); + + totalNumber = getNumArpEntries(); + + status = openTcpFile( &tcpFile ); + + if( !NT_SUCCESS(status) ) { + TRACE("failure: %08x\n", (int)status ); + return 0; + } + + IpArpTable = HeapAlloc + ( GetProcessHeap(), 0, + sizeof(DWORD) + (sizeof(MIB_IPNETROW) * totalNumber) ); + + status = tdiGetEntityIDSet( tcpFile, &entitySet, &numEntities ); + + for( i = 0; i < numEntities; i++ ) { + if( isIpEntity( tcpFile, &entitySet[i] ) && + hasArp( tcpFile, &entitySet[i] ) ) { + + status = tdiGetSetOfThings( tcpFile, + INFO_CLASS_PROTOCOL, + INFO_TYPE_PROVIDER, + IP_MIB_ARPTABLE_ENTRY_ID, + AT_ENTITY, + entitySet[i].tei_instance, + 0, + sizeof(MIB_IPNETROW), + (PVOID *)&AdapterArpTable, + &returnSize ); + + if( status == STATUS_SUCCESS ) { + for( row = 0; row < returnSize; row++ ) + IpArpTable->table[row] = AdapterArpTable[row]; + } + + if( AdapterArpTable ) tdiFreeThingSet( AdapterArpTable ); + } + } + + tdiFreeThingSet( entitySet ); + IpArpTable->dwNumEntries = row; + + return IpArpTable; } DWORD getNumUdpEntries(void) diff --git a/reactos/drivers/network/tcpip/include/info.h b/reactos/drivers/network/tcpip/include/info.h index eb066773b21..50cfa9a5bc2 100644 --- a/reactos/drivers/network/tcpip/include/info.h +++ b/reactos/drivers/network/tcpip/include/info.h @@ -46,6 +46,19 @@ typedef struct IPADDR_ENTRY { USHORT Pad; } IPADDR_ENTRY, *PIPADDR_ENTRY; +#define ARP_ENTRY_STATIC 4 +#define ARP_ENTRY_DYNAMIC 3 +#define ARP_ENTRY_INVALID 2 +#define ARP_ENTRY_OTHER 1 + +typedef struct IPARP_ENTRY { + ULONG Index; + ULONG AddrSize; + UCHAR PhysAddr[8]; + ULONG LogAddr; + ULONG Type; +} IPARP_ENTRY, *PIPARP_ENTRY; + typedef struct IPROUTE_ENTRY { ULONG Dest; ULONG Index; //matches if_index in IFEntry and iae_index in IPAddrEntry @@ -96,6 +109,9 @@ typedef struct IFENTRY { #ifndef IP_MIB_ADDRTABLE_ENTRY_ID #define IP_MIB_ADDRTABLE_ENTRY_ID 0x102 #endif +#ifndef IP_MIB_ARPTABLE_ENTRY_ID +#define IP_MIB_ARPTABLE_ENTRY_ID 0x101 +#endif #ifndef MAX_PHYSADDR_SIZE #define MAX_PHYSADDR_SIZE 8 #endif diff --git a/reactos/drivers/network/tcpip/include/neighbor.h b/reactos/drivers/network/tcpip/include/neighbor.h index 4d30cb346ec..aa1859426a9 100644 --- a/reactos/drivers/network/tcpip/include/neighbor.h +++ b/reactos/drivers/network/tcpip/include/neighbor.h @@ -107,6 +107,10 @@ BOOLEAN NBQueuePacket( VOID NBRemoveNeighbor( PNEIGHBOR_CACHE_ENTRY NCE); +DWORD NBCopyNeighbors( + PIP_INTERFACE Interface, + PIPARP_ENTRY ArpTable); + #endif /* __NEIGHBOR_H */ /* EOF */ diff --git a/reactos/drivers/network/tcpip/include/precomp.h b/reactos/drivers/network/tcpip/include/precomp.h index 680d9dd01ec..5906ced640f 100644 --- a/reactos/drivers/network/tcpip/include/precomp.h +++ b/reactos/drivers/network/tcpip/include/precomp.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/reactos/drivers/network/tcpip/tcpip/iinfo.c b/reactos/drivers/network/tcpip/tcpip/iinfo.c index 8e3eaf97e52..7a6fe06f49c 100644 --- a/reactos/drivers/network/tcpip/tcpip/iinfo.c +++ b/reactos/drivers/network/tcpip/tcpip/iinfo.c @@ -78,6 +78,42 @@ TDI_STATUS InfoTdiQueryGetInterfaceMIB(TDIEntityID *ID, return Status; } +TDI_STATUS InfoTdiQueryGetArptableMIB(TDIEntityID *ID, + PIP_INTERFACE Interface, + PNDIS_BUFFER Buffer, + PUINT BufferSize) { + NTSTATUS Status; + DWORD NumNeighbors = NBCopyNeighbors( Interface, NULL ); + DWORD MemSize = NumNeighbors * sizeof(IPARP_ENTRY); + PIPARP_ENTRY ArpEntries = + ExAllocatePoolWithTag + ( NonPagedPool, MemSize, FOURCC('A','R','P','t') ); + + if( !ArpEntries ) return STATUS_NO_MEMORY; + NBCopyNeighbors( Interface, ArpEntries ); + + Status = InfoCopyOut( (PVOID)ArpEntries, MemSize, Buffer, BufferSize ); + + ExFreePool( ArpEntries ); + + return Status; +} + +TDI_STATUS InfoTdiQueryGetArpCapability(TDIEntityID *ID, + PIP_INTERFACE Interface, + PNDIS_BUFFER Buffer, + PUINT BufferSize) { + NTSTATUS Status; + DWORD Capability = 0x280; + + TI_DbgPrint(MID_TRACE,("Copying out %d bytes (AT_ENTITY capability)\n", + sizeof(Capability))); + Status = InfoCopyOut + ( (PVOID)&Capability, sizeof(Capability), Buffer, BufferSize ); + + return Status; +} + TDI_STATUS InfoInterfaceTdiQueryEx( UINT InfoClass, UINT InfoType, UINT InfoId, @@ -87,13 +123,22 @@ TDI_STATUS InfoInterfaceTdiQueryEx( UINT InfoClass, PUINT BufferSize ) { if( InfoClass == INFO_CLASS_GENERIC && InfoType == INFO_TYPE_PROVIDER && - InfoId == ENTITY_TYPE_ID ) { - ULONG Temp = IF_MIB; - return InfoCopyOut( (PCHAR)&Temp, sizeof(Temp), Buffer, BufferSize ); + InfoId == ENTITY_TYPE_ID && + id->tei_entity == AT_ENTITY ) { + return InfoTdiQueryGetArpCapability( id, Context, Buffer, BufferSize ); } else if( InfoClass == INFO_CLASS_PROTOCOL && InfoType == INFO_TYPE_PROVIDER && InfoId == IF_MIB_STATS_ID ) { return InfoTdiQueryGetInterfaceMIB( id, Context, Buffer, BufferSize ); + } else if( InfoClass == INFO_CLASS_GENERIC && + InfoType == INFO_TYPE_PROVIDER && + InfoId == ENTITY_TYPE_ID ) { + ULONG Temp = IF_MIB; + return InfoCopyOut( (PCHAR)&Temp, sizeof(Temp), Buffer, BufferSize ); + } else if( InfoClass == INFO_CLASS_PROTOCOL && + InfoType == INFO_TYPE_PROVIDER && + InfoId == IP_MIB_ARPTABLE_ENTRY_ID ) { + return InfoTdiQueryGetArptableMIB( id, Context, Buffer, BufferSize ); } else return TDI_INVALID_REQUEST; } diff --git a/reactos/drivers/network/tcpip/tcpip/info.c b/reactos/drivers/network/tcpip/tcpip/info.c index 10d466180cb..fb4305c5919 100644 --- a/reactos/drivers/network/tcpip/tcpip/info.c +++ b/reactos/drivers/network/tcpip/tcpip/info.c @@ -164,6 +164,35 @@ TDI_STATUS InfoTdiQueryInformationEx( Status = TDI_INVALID_PARAMETER; } else Status = InfoTdiQueryListEntities(Buffer, BufferSize); + } else if (ID->toi_entity.tei_entity == AT_ENTITY) { + TcpipAcquireSpinLock( &EntityListLock, &OldIrql ); + + for( i = 0; i < EntityCount; i++ ) { + if( EntityList[i].tei_entity == IF_ENTITY && + EntityList[i].tei_instance == ID->toi_entity.tei_instance ) { + InfoRequest = EntityList[i].info_req; + context = EntityList[i].context; + FoundEntity = TRUE; + break; + } + } + + TcpipReleaseSpinLock( &EntityListLock, OldIrql ); + + if( FoundEntity ) { + TI_DbgPrint(DEBUG_INFO, + ("Calling AT Entity %d (%04x:%d) InfoEx (%x,%x,%x)\n", + i, ID->toi_entity.tei_entity, + ID->toi_entity.tei_instance, + ID->toi_class, ID->toi_type, ID->toi_id)); + Status = InfoRequest( ID->toi_class, + ID->toi_type, + ID->toi_id, + context, + &ID->toi_entity, + Buffer, + BufferSize ); + } } else { TcpipAcquireSpinLock( &EntityListLock, &OldIrql ); diff --git a/reactos/include/psdk/tcpioctl.h b/reactos/include/psdk/tcpioctl.h index 708641d6faf..d04411ff707 100644 --- a/reactos/include/psdk/tcpioctl.h +++ b/reactos/include/psdk/tcpioctl.h @@ -45,6 +45,7 @@ #define IF_MIB_STATS_ID 1 #define IP_MIB_STATS_ID 1 +#define IP_MIB_ARPTABLE_ENTRY_ID 0x101 #define IP_MIB_ADDRTABLE_ENTRY_ID 0x102 #define IP_INTFC_INFO_ID 0x103 #define MAX_PHYSADDR_SIZE 8 diff --git a/reactos/lib/drivers/ip/network/neighbor.c b/reactos/lib/drivers/ip/network/neighbor.c index 6d5248a67ae..330caa48117 100644 --- a/reactos/lib/drivers/ip/network/neighbor.c +++ b/reactos/lib/drivers/ip/network/neighbor.c @@ -514,3 +514,43 @@ VOID NBRemoveNeighbor( TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql); } + +ULONG NBCopyNeighbors +(PIP_INTERFACE Interface, + PIPARP_ENTRY ArpTable) +{ + PNEIGHBOR_CACHE_ENTRY CurNCE; + KIRQL OldIrql; + UINT Size = 0, i; + + for (i = 0; i <= NB_HASHMASK; i++) { + TcpipAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql); + for( CurNCE = NeighborCache[i].Cache; + CurNCE; + CurNCE = CurNCE->Next ) { + if( CurNCE->Interface == Interface ) { + if( ArpTable ) { + ArpTable[Size].Index = Interface->Index; + ArpTable[Size].AddrSize = CurNCE->LinkAddressLength; + RtlCopyMemory + (ArpTable[Size].PhysAddr, + CurNCE->LinkAddress, + CurNCE->LinkAddressLength); + ArpTable[Size].LogAddr = CurNCE->Address.Address.IPv4Address; + if( CurNCE->State & NUD_PERMANENT ) + ArpTable[Size].Type = ARP_ENTRY_STATIC; + else if( CurNCE->State & NUD_CONNECTED ) + ArpTable[Size].Type = ARP_ENTRY_DYNAMIC; + else if( !(CurNCE->State & NUD_VALID) ) + ArpTable[Size].Type = ARP_ENTRY_INVALID; + else + ArpTable[Size].Type = ARP_ENTRY_OTHER; + } + Size++; + } + } + TcpipReleaseSpinLock(&NeighborCache[i].Lock, OldIrql); + } + + return Size; +}