1 /* Copyright (C) 2003 Art Yerkes
2 * A reimplementation of ifenum.c by Juan Lang
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 * Implementation notes
19 * - Our bretheren use IOCTL_TCP_QUERY_INFORMATION_EX to get information
20 * from tcpip.sys and IOCTL_TCP_SET_INFORMATION_EX to set info (such as
21 * the route table). These ioctls mirror WsControl exactly in usage.
22 * - This iphlpapi does not rely on any reactos-only features.
23 * - This implementation is meant to be largely correct. I am not, however,
24 * paying any attention to performance. It can be done faster, and
25 * someone should definately optimize this code when speed is more of a
26 * priority than it is now.
28 * Edited implementation notes from the original -- Basically edited to add
29 * information and prune things which are not accurate about this file.
30 * Interface index fun:
31 * - Windows may rely on an index being cleared in the topmost 8 bits in some
32 * APIs; see GetFriendlyIfIndex and the mention of "backward compatible"
33 * indexes. It isn't clear which APIs might fail with non-backward-compatible
34 * indexes, but I'll keep them bits clear just in case.
36 * - We don't support IPv6 addresses here yet -- I moved the upper edge
37 * functions into iphlpv6.c (arty)
39 #include "iphlpapi_private.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi
);
45 /* I'm a bit skittish about maintaining this info in memory, as I'd rather
46 * not add any mutex or critical section blockers to these functions. I've
47 * encountered far too many windows functions that contribute to deadlock
48 * by not announcing themselves. */
49 void interfaceMapInit(void)
51 /* For now, nothing */
54 void interfaceMapFree(void)
59 NTSTATUS tdiGetMibForIfEntity
60 ( HANDLE tcpFile
, TDIEntityID
*ent
, IFEntrySafelySized
*entry
) {
61 TCP_REQUEST_QUERY_INFORMATION_EX req
= TCP_REQUEST_QUERY_INFORMATION_INIT
;
62 NTSTATUS status
= STATUS_SUCCESS
;
65 WARN("TdiGetMibForIfEntity(tcpFile %p,entityId %p)\n",
66 tcpFile
, ent
->tei_instance
);
68 req
.ID
.toi_class
= INFO_CLASS_PROTOCOL
;
69 req
.ID
.toi_type
= INFO_TYPE_PROVIDER
;
70 req
.ID
.toi_id
= IF_MIB_STATS_ID
;
71 req
.ID
.toi_entity
= *ent
;
73 status
= DeviceIoControl( tcpFile
,
74 IOCTL_TCP_QUERY_INFORMATION_EX
,
84 WARN("IOCTL Failed\n");
85 return STATUS_UNSUCCESSFUL
;
88 TRACE("TdiGetMibForIfEntity() => {\n"
89 " if_index ....................... %x\n"
90 " if_type ........................ %x\n"
91 " if_mtu ......................... %d\n"
92 " if_speed ....................... %x\n"
93 " if_physaddrlen ................. %d\n",
98 entry
->ent
.if_physaddrlen
);
99 TRACE(" if_physaddr .................... %02x:%02x:%02x:%02x:%02x:%02x\n"
100 " if_descr ....................... %*s\n",
101 entry
->ent
.if_physaddr
[0] & 0xff,
102 entry
->ent
.if_physaddr
[1] & 0xff,
103 entry
->ent
.if_physaddr
[2] & 0xff,
104 entry
->ent
.if_physaddr
[3] & 0xff,
105 entry
->ent
.if_physaddr
[4] & 0xff,
106 entry
->ent
.if_physaddr
[5] & 0xff,
107 entry
->ent
.if_descrlen
,
108 entry
->ent
.if_descr
);
109 TRACE("} status %08x\n",status
);
111 return STATUS_SUCCESS
;
114 BOOL
isInterface( TDIEntityID
*if_maybe
) {
116 if_maybe
->tei_entity
== IF_ENTITY
;
119 BOOL
isLoopback( HANDLE tcpFile
, TDIEntityID
*loop_maybe
) {
120 IFEntrySafelySized entryInfo
;
123 status
= tdiGetMibForIfEntity( tcpFile
,
127 return NT_SUCCESS(status
) &&
128 (entryInfo
.ent
.if_type
== IFENT_SOFTWARE_LOOPBACK
);
131 BOOL
hasArp( HANDLE tcpFile
, TDIEntityID
*arp_maybe
) {
132 TCP_REQUEST_QUERY_INFORMATION_EX req
= TCP_REQUEST_QUERY_INFORMATION_INIT
;
133 NTSTATUS status
= STATUS_SUCCESS
;
134 DWORD returnSize
, type
;
136 req
.ID
.toi_class
= INFO_CLASS_GENERIC
;
137 req
.ID
.toi_type
= INFO_TYPE_PROVIDER
;
138 req
.ID
.toi_id
= ENTITY_TYPE_ID
;
139 req
.ID
.toi_entity
.tei_entity
= AT_ENTITY
;
140 req
.ID
.toi_entity
.tei_instance
= arp_maybe
->tei_instance
;
142 status
= DeviceIoControl( tcpFile
,
143 IOCTL_TCP_QUERY_INFORMATION_EX
,
150 if( !NT_SUCCESS(status
) ) return FALSE
;
152 return (type
& AT_ARP
);
155 static NTSTATUS
getInterfaceInfoSet( HANDLE tcpFile
,
157 PDWORD numInterfaces
) {
159 TDIEntityID
*entIDSet
= NULL
;
160 NTSTATUS status
= tdiGetEntityIDSet( tcpFile
, &entIDSet
, &numEntities
);
161 IFInfo
*infoSetInt
= 0;
162 int curInterf
= 0, i
;
164 if (!NT_SUCCESS(status
)) {
165 ERR("getInterfaceInfoSet: tdiGetEntityIDSet() failed: 0x%lx\n", status
);
169 infoSetInt
= HeapAlloc( GetProcessHeap(), 0,
170 sizeof(IFInfo
) * numEntities
);
173 for( i
= 0; i
< numEntities
; i
++ ) {
174 if( isInterface( &entIDSet
[i
] ) ) {
175 infoSetInt
[curInterf
].entity_id
= entIDSet
[i
];
176 status
= tdiGetMibForIfEntity
179 &infoSetInt
[curInterf
].if_info
);
180 TRACE("tdiGetMibForIfEntity: %08x\n", status
);
181 if( NT_SUCCESS(status
) ) {
187 status
= getNthIpEntity( tcpFile
, curInterf
, &ip_ent
);
188 if( NT_SUCCESS(status
) )
189 status
= tdiGetIpAddrsForIpEntity
190 ( tcpFile
, &ip_ent
, &addrs
, &numAddrs
);
191 for( j
= 0; NT_SUCCESS(status
) && j
< numAddrs
; j
++ ) {
192 TRACE("ADDR %d: index %d (target %d)\n", j
, addrs
[j
].iae_index
, infoSetInt
[curInterf
].if_info
.ent
.if_index
);
193 if( addrs
[j
].iae_index
==
194 infoSetInt
[curInterf
].if_info
.ent
.if_index
) {
195 memcpy( &infoSetInt
[curInterf
].ip_addr
,
197 sizeof( addrs
[j
] ) );
202 if ( NT_SUCCESS(status
) )
203 tdiFreeThingSet(addrs
);
208 tdiFreeThingSet(entIDSet
);
210 if (NT_SUCCESS(status
)) {
211 *infoSet
= infoSetInt
;
212 *numInterfaces
= curInterf
;
214 HeapFree(GetProcessHeap(), 0, infoSetInt
);
219 tdiFreeThingSet(entIDSet
);
220 return STATUS_INSUFFICIENT_RESOURCES
;
224 static DWORD
getNumInterfacesInt(BOOL onlyNonLoopback
)
226 DWORD numEntities
, numInterfaces
= 0;
227 TDIEntityID
*entitySet
;
232 status
= openTcpFile( &tcpFile
, FILE_READ_DATA
);
234 if( !NT_SUCCESS(status
) ) {
235 WARN("getNumInterfaces: failed %08x\n", status
);
239 status
= tdiGetEntityIDSet( tcpFile
, &entitySet
, &numEntities
);
241 if( !NT_SUCCESS(status
) ) {
242 WARN("getNumInterfaces: failed %08x\n", status
);
243 closeTcpFile( tcpFile
);
247 for( i
= 0; i
< numEntities
; i
++ ) {
248 if( isInterface( &entitySet
[i
] ) &&
250 (onlyNonLoopback
&& !isLoopback( tcpFile
, &entitySet
[i
] ))) )
254 TRACE("getNumInterfaces: success: %d %d %08x\n",
255 onlyNonLoopback
, numInterfaces
, status
);
257 closeTcpFile( tcpFile
);
259 tdiFreeThingSet( entitySet
);
261 return numInterfaces
;
264 DWORD
getNumInterfaces(void)
266 return getNumInterfacesInt( FALSE
);
269 DWORD
getNumNonLoopbackInterfaces(void)
271 return getNumInterfacesInt( TRUE
);
274 DWORD
getNthInterfaceEntity( HANDLE tcpFile
, DWORD index
, TDIEntityID
*ent
) {
275 DWORD numEntities
= 0;
276 DWORD numInterfaces
= 0;
277 TDIEntityID
*entitySet
= 0;
278 NTSTATUS status
= tdiGetEntityIDSet( tcpFile
, &entitySet
, &numEntities
);
281 if( !NT_SUCCESS(status
) )
284 for( i
= 0; i
< numEntities
; i
++ ) {
285 if( isInterface( &entitySet
[i
] ) ) {
286 if( numInterfaces
== index
) break;
287 else numInterfaces
++;
291 TRACE("Index %d is entity #%d - %04x:%08x\n", index
, i
,
292 entitySet
[i
].tei_entity
, entitySet
[i
].tei_instance
);
294 if( numInterfaces
== index
&& i
< numEntities
) {
295 memcpy( ent
, &entitySet
[i
], sizeof(*ent
) );
296 tdiFreeThingSet( entitySet
);
297 return STATUS_SUCCESS
;
299 tdiFreeThingSet( entitySet
);
300 return STATUS_UNSUCCESSFUL
;
304 NTSTATUS
getInterfaceInfoByIndex( HANDLE tcpFile
, DWORD index
, IFInfo
*info
) {
307 NTSTATUS status
= getInterfaceInfoSet( tcpFile
, &ifInfo
, &numInterfaces
);
310 if( NT_SUCCESS(status
) )
312 for( i
= 0; i
< numInterfaces
; i
++ ) {
313 if( ifInfo
[i
].if_info
.ent
.if_index
== index
) {
314 memcpy( info
, &ifInfo
[i
], sizeof(*info
) );
319 HeapFree(GetProcessHeap(), 0, ifInfo
);
321 return i
< numInterfaces
? STATUS_SUCCESS
: STATUS_UNSUCCESSFUL
;
327 NTSTATUS
getInterfaceInfoByName( HANDLE tcpFile
, char *name
, IFInfo
*info
) {
331 NTSTATUS status
= getInterfaceInfoSet( tcpFile
, &ifInfo
, &numInterfaces
);
333 if( NT_SUCCESS(status
) )
335 for( i
= 0; i
< numInterfaces
; i
++ ) {
336 if( !strncmp((PCHAR
)ifInfo
[i
].if_info
.ent
.if_descr
, name
, ifInfo
[i
].if_info
.ent
.if_descrlen
) ) {
337 memcpy( info
, &ifInfo
[i
], sizeof(*info
) );
342 HeapFree(GetProcessHeap(), 0,ifInfo
);
344 return i
< numInterfaces
? STATUS_SUCCESS
: STATUS_UNSUCCESSFUL
;
350 /* Note that the result of this operation must be freed later */
352 const char *getInterfaceNameByIndex(DWORD index
)
356 char *interfaceName
= NULL
;
357 NTSTATUS status
= openTcpFile( &tcpFile
, FILE_READ_DATA
);
359 if( NT_SUCCESS(status
) ) {
360 status
= getInterfaceInfoByIndex( tcpFile
, index
, &ifInfo
);
362 if( NT_SUCCESS(status
) ) {
363 interfaceName
= HeapAlloc( GetProcessHeap(), 0,
364 ifInfo
.if_info
.ent
.if_descrlen
+ 1 );
365 if( interfaceName
) {
366 memcpy(interfaceName
, ifInfo
.if_info
.ent
.if_descr
, ifInfo
.if_info
.ent
.if_descrlen
);
367 interfaceName
[ifInfo
.if_info
.ent
.if_descrlen
] = '\0';
371 closeTcpFile( tcpFile
);
374 return interfaceName
;
377 void consumeInterfaceName(const char *name
) {
378 HeapFree( GetProcessHeap(), 0, (char *)name
);
381 DWORD
getInterfaceIndexByName(const char *name
, PDWORD index
)
385 NTSTATUS status
= openTcpFile( &tcpFile
, FILE_READ_DATA
);
387 if( NT_SUCCESS(status
) ) {
388 status
= getInterfaceInfoByName( tcpFile
, (char *)name
, &ifInfo
);
390 if( NT_SUCCESS(status
) ) {
391 *index
= ifInfo
.if_info
.ent
.if_index
;
394 closeTcpFile( tcpFile
);
400 InterfaceIndexTable
*getInterfaceIndexTableInt( BOOL nonLoopbackOnly
) {
401 DWORD numInterfaces
, curInterface
= 0;
404 InterfaceIndexTable
*ret
= 0;
406 NTSTATUS status
= openTcpFile( &tcpFile
, FILE_READ_DATA
);
408 if( NT_SUCCESS(status
) ) {
409 status
= getInterfaceInfoSet( tcpFile
, &ifInfo
, &numInterfaces
);
411 TRACE("InterfaceInfoSet: %08x, %04x:%08x\n",
413 ifInfo
->entity_id
.tei_entity
,
414 ifInfo
->entity_id
.tei_instance
);
416 if( NT_SUCCESS(status
) ) {
417 ret
= (InterfaceIndexTable
*)
419 sizeof(InterfaceIndexTable
) +
420 (numInterfaces
- 1) * sizeof(DWORD
));
423 ret
->numAllocated
= numInterfaces
;
424 TRACE("NumInterfaces = %d\n", numInterfaces
);
426 for( i
= 0; i
< numInterfaces
; i
++ ) {
427 TRACE("Examining interface %d\n", i
);
428 if( !nonLoopbackOnly
||
429 !isLoopback( tcpFile
, &ifInfo
[i
].entity_id
) ) {
430 TRACE("Interface %d matches (%d)\n", i
, curInterface
);
431 ret
->indexes
[curInterface
++] =
432 ifInfo
[i
].if_info
.ent
.if_index
;
436 ret
->numIndexes
= curInterface
;
439 tdiFreeThingSet( ifInfo
);
441 closeTcpFile( tcpFile
);
447 InterfaceIndexTable
*getInterfaceIndexTable(void) {
448 return getInterfaceIndexTableInt( FALSE
);
451 InterfaceIndexTable
*getNonLoopbackInterfaceIndexTable(void) {
452 return getInterfaceIndexTableInt( TRUE
);
455 DWORD
getInterfaceIPAddrByName(const char *name
)
460 NTSTATUS
getIPAddrEntryForIf(HANDLE tcpFile
,
466 getInterfaceInfoByName( tcpFile
, name
, ifInfo
) :
467 getInterfaceInfoByIndex( tcpFile
, index
, ifInfo
);
469 if (!NT_SUCCESS(status
)) {
470 ERR("getIPAddrEntryForIf returning %lx\n", status
);
476 DWORD
getAddrByIndexOrName( char *name
, DWORD index
, IPHLPAddrType addrType
) {
479 NTSTATUS status
= STATUS_SUCCESS
;
480 DWORD addrOut
= INADDR_ANY
;
482 status
= openTcpFile( &tcpFile
, FILE_READ_DATA
);
484 if( NT_SUCCESS(status
) ) {
485 status
= getIPAddrEntryForIf( tcpFile
, name
, index
, &ifInfo
);
486 if( NT_SUCCESS(status
) ) {
488 case IPAAddr
: addrOut
= ifInfo
.ip_addr
.iae_addr
; break;
489 case IPABcast
: addrOut
= ifInfo
.ip_addr
.iae_bcastaddr
; break;
490 case IPAMask
: addrOut
= ifInfo
.ip_addr
.iae_mask
; break;
491 case IFMtu
: addrOut
= ifInfo
.if_info
.ent
.if_mtu
; break;
492 case IFStatus
: addrOut
= ifInfo
.if_info
.ent
.if_operstatus
; break;
495 closeTcpFile( tcpFile
);
501 DWORD
getInterfaceIPAddrByIndex(DWORD index
) {
502 return getAddrByIndexOrName( 0, index
, IPAAddr
);
505 DWORD
getInterfaceBCastAddrByName(const char *name
) {
506 return getAddrByIndexOrName( (char *)name
, 0, IPABcast
);
509 DWORD
getInterfaceBCastAddrByIndex(DWORD index
) {
510 return getAddrByIndexOrName( 0, index
, IPABcast
);
513 DWORD
getInterfaceMaskByName(const char *name
) {
514 return getAddrByIndexOrName( (char *)name
, 0, IPAMask
);
517 DWORD
getInterfaceMaskByIndex(DWORD index
) {
518 return getAddrByIndexOrName( 0, index
, IPAMask
);
521 void getInterfacePhysicalFromInfo( IFInfo
*info
,
522 PDWORD len
, PBYTE addr
, PDWORD type
) {
523 *len
= info
->if_info
.ent
.if_physaddrlen
;
524 memcpy( addr
, info
->if_info
.ent
.if_physaddr
, *len
);
525 *type
= info
->if_info
.ent
.if_type
;
528 DWORD
getInterfacePhysicalByName(const char *name
, PDWORD len
, PBYTE addr
,
533 NTSTATUS status
= openTcpFile( &tcpFile
, FILE_READ_DATA
);
535 if( NT_SUCCESS(status
) ) {
536 status
= getInterfaceInfoByName( tcpFile
, (char *)name
, &info
);
537 if( NT_SUCCESS(status
) )
538 getInterfacePhysicalFromInfo( &info
, len
, addr
, type
);
539 closeTcpFile( tcpFile
);
545 DWORD
getInterfacePhysicalByIndex(DWORD index
, PDWORD len
, PBYTE addr
,
550 NTSTATUS status
= openTcpFile( &tcpFile
, FILE_READ_DATA
);
552 if( NT_SUCCESS(status
) ) {
553 status
= getInterfaceInfoByIndex( tcpFile
, index
, &info
);
554 if( NT_SUCCESS(status
) )
555 getInterfacePhysicalFromInfo( &info
, len
, addr
, type
);
556 closeTcpFile( tcpFile
);
562 DWORD
getInterfaceMtuByName(const char *name
, PDWORD mtu
) {
563 *mtu
= getAddrByIndexOrName( (char *)name
, 0, IFMtu
);
564 return STATUS_SUCCESS
;
567 DWORD
getInterfaceMtuByIndex(DWORD index
, PDWORD mtu
) {
568 *mtu
= getAddrByIndexOrName( 0, index
, IFMtu
);
569 return STATUS_SUCCESS
;
572 DWORD
getInterfaceStatusByName(const char *name
, PDWORD status
) {
573 *status
= getAddrByIndexOrName( (char *)name
, 0, IFStatus
);
574 return STATUS_SUCCESS
;
577 DWORD
getInterfaceStatusByIndex(DWORD index
, PDWORD status
)
579 *status
= getAddrByIndexOrName( 0, index
, IFStatus
);
580 return STATUS_SUCCESS
;
583 DWORD
getInterfaceEntryByName(const char *name
, PMIB_IFROW entry
)
587 NTSTATUS status
= openTcpFile( &tcpFile
, FILE_READ_DATA
);
591 if( NT_SUCCESS(status
) ) {
592 status
= getInterfaceInfoByName( tcpFile
, (char *)name
, &info
);
594 if( NT_SUCCESS(status
) ) {
595 memcpy( &entry
->wszName
[MAX_INTERFACE_NAME_LEN
],
597 sizeof(info
.if_info
) );
600 TRACE("entry->bDescr = %s\n", entry
->bDescr
);
602 closeTcpFile( tcpFile
);
608 DWORD
getInterfaceEntryByIndex(DWORD index
, PMIB_IFROW entry
)
612 NTSTATUS status
= openTcpFile( &tcpFile
, FILE_READ_DATA
);
616 if( NT_SUCCESS(status
) ) {
617 status
= getInterfaceInfoByIndex( tcpFile
, index
, &info
);
619 if( NT_SUCCESS(status
) ) {
620 memcpy( &entry
->wszName
[MAX_INTERFACE_NAME_LEN
],
622 sizeof(info
.if_info
) );
625 closeTcpFile( tcpFile
);
631 char *toIPAddressString(unsigned int addr
, char string
[16])
633 struct in_addr iAddr
;
638 strncpy(string
, inet_ntoa(iAddr
), 16);
640 return inet_ntoa(iAddr
);
643 NTSTATUS
addIPAddress( IPAddr Address
, IPMask Mask
, DWORD IfIndex
,
644 PULONG NteContext
, PULONG NteInstance
)
647 NTSTATUS status
= openTcpFile( &tcpFile
, FILE_READ_DATA
| FILE_WRITE_DATA
);
649 IO_STATUS_BLOCK Iosb
;
653 if( !NT_SUCCESS(status
) ) return status
;
655 Data
.NteContext
= IfIndex
;
656 Data
.NewAddress
= Address
;
657 Data
.NewNetmask
= Mask
;
659 status
= NtDeviceIoControlFile( tcpFile
,
664 IOCTL_SET_IP_ADDRESS
,
670 closeTcpFile( tcpFile
);
672 if( NT_SUCCESS(status
) ) {
673 *NteContext
= Iosb
.Information
;
674 *NteInstance
= Data
.NewAddress
;
677 if (!NT_SUCCESS(status
)) {
678 ERR("addIPAddress for if %d returning 0x%lx\n", IfIndex
, status
);
685 NTSTATUS
deleteIpAddress( ULONG NteContext
)
688 NTSTATUS status
= openTcpFile( &tcpFile
, FILE_READ_DATA
| FILE_WRITE_DATA
);
689 IO_STATUS_BLOCK Iosb
;
693 if( !NT_SUCCESS(status
) ) return status
;
695 status
= NtDeviceIoControlFile( tcpFile
,
700 IOCTL_DELETE_IP_ADDRESS
,
706 closeTcpFile( tcpFile
);
708 if (!NT_SUCCESS(status
)) {
709 ERR("deleteIpAddress(%lu) returning 0x%lx\n", NteContext
, status
);