1 /* Copyright (C) 2003,2006 Juan Lang
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * Implementation notes
19 * - I don't support IPv6 addresses here, since SIOCGIFCONF can't return them
21 * There are three implemented methods for determining the MAC address of an
23 * - a specific IOCTL (Linux)
24 * - looking in the ARP cache (at least Solaris)
25 * - using the sysctl interface (FreeBSD and MacOSX)
26 * Solaris and some others have SIOCGENADDR, but I haven't gotten that to work
27 * on the Solaris boxes at SourceForge's compile farm, whereas SIOCGARP does.
41 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
47 #ifdef HAVE_NETINET_IN_H
48 #include <netinet/in.h>
51 #ifdef HAVE_ARPA_INET_H
52 #include <arpa/inet.h>
59 #ifdef HAVE_NET_IF_ARP_H
60 #include <net/if_arp.h>
63 #ifdef HAVE_NET_ROUTE_H
64 #include <net/route.h>
67 #ifdef HAVE_SYS_IOCTL_H
68 #include <sys/ioctl.h>
71 #ifdef HAVE_SYS_SYSCTL_H
72 #include <sys/sysctl.h>
75 #ifdef HAVE_SYS_SOCKIO_H
76 #include <sys/sockio.h>
79 #ifdef HAVE_NET_IF_DL_H
80 #include <net/if_dl.h>
83 #ifdef HAVE_NET_IF_TYPES_H
84 #include <net/if_types.h>
89 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
90 #define ifreq_len(ifr) \
91 max(sizeof(struct ifreq), sizeof((ifr)->ifr_name)+(ifr)->ifr_addr.sa_len)
93 #define ifreq_len(ifr) sizeof(struct ifreq)
101 #define IF_NAMESIZE 16
105 #define INADDR_NONE (~0U)
108 #define INITIAL_INTERFACES_ASSUMED 4
110 #define INDEX_IS_LOOPBACK 0x00800000
114 static int isLoopbackInterface(int fd
, const char *name
)
121 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
122 if (ioctl(fd
, SIOCGIFFLAGS
, &ifr
) == 0)
123 ret
= ifr
.ifr_flags
& IFF_LOOPBACK
;
128 /* The comments say MAX_ADAPTER_NAME is required, but really only IF_NAMESIZE
129 * bytes are necessary.
131 char *getInterfaceNameByIndex(DWORD index
, char *name
)
133 return if_indextoname(index
, name
);
136 DWORD
getInterfaceIndexByName(const char *name
, PDWORD index
)
142 return ERROR_INVALID_PARAMETER
;
144 return ERROR_INVALID_PARAMETER
;
145 idx
= if_nametoindex(name
);
151 ret
= ERROR_INVALID_DATA
;
155 DWORD
getNumNonLoopbackInterfaces(void)
158 int fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
161 struct if_nameindex
*indexes
= if_nameindex();
164 struct if_nameindex
*p
;
166 for (p
= indexes
, numInterfaces
= 0; p
&& p
->if_name
; p
++)
167 if (!isLoopbackInterface(fd
, p
->if_name
))
169 if_freenameindex(indexes
);
177 return numInterfaces
;
180 DWORD
getNumInterfaces(void)
183 struct if_nameindex
*indexes
= if_nameindex();
186 struct if_nameindex
*p
;
188 for (p
= indexes
, numInterfaces
= 0; p
&& p
->if_name
; p
++)
190 if_freenameindex(indexes
);
194 return numInterfaces
;
197 InterfaceIndexTable
*getInterfaceIndexTable(void)
200 InterfaceIndexTable
*ret
;
201 struct if_nameindex
*indexes
= if_nameindex();
204 struct if_nameindex
*p
;
206 for (p
= indexes
, numInterfaces
= 0; p
&& p
->if_name
; p
++)
208 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
209 sizeof(InterfaceIndexTable
) + (numInterfaces
- 1) * sizeof(DWORD
));
211 for (p
= indexes
; p
&& p
->if_name
; p
++)
212 ret
->indexes
[ret
->numIndexes
++] = p
->if_index
;
214 if_freenameindex(indexes
);
221 InterfaceIndexTable
*getNonLoopbackInterfaceIndexTable(void)
224 InterfaceIndexTable
*ret
;
225 int fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
228 struct if_nameindex
*indexes
= if_nameindex();
231 struct if_nameindex
*p
;
233 for (p
= indexes
, numInterfaces
= 0; p
&& p
->if_name
; p
++)
234 if (!isLoopbackInterface(fd
, p
->if_name
))
236 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
237 sizeof(InterfaceIndexTable
) + (numInterfaces
- 1) * sizeof(DWORD
));
239 for (p
= indexes
; p
&& p
->if_name
; p
++)
240 if (!isLoopbackInterface(fd
, p
->if_name
))
241 ret
->indexes
[ret
->numIndexes
++] = p
->if_index
;
243 if_freenameindex(indexes
);
254 static DWORD
getInterfaceBCastAddrByName(const char *name
)
256 DWORD ret
= INADDR_ANY
;
259 int fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
264 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
265 if (ioctl(fd
, SIOCGIFBRDADDR
, &ifr
) == 0)
266 memcpy(&ret
, ifr
.ifr_addr
.sa_data
+ 2, sizeof(DWORD
));
273 static DWORD
getInterfaceMaskByName(const char *name
)
275 DWORD ret
= INADDR_NONE
;
278 int fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
283 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
284 if (ioctl(fd
, SIOCGIFNETMASK
, &ifr
) == 0)
285 memcpy(&ret
, ifr
.ifr_addr
.sa_data
+ 2, sizeof(DWORD
));
292 #if defined (SIOCGIFHWADDR)
293 DWORD
getInterfacePhysicalByName(const char *name
, PDWORD len
, PBYTE addr
,
299 if (!name
|| !len
|| !addr
|| !type
)
300 return ERROR_INVALID_PARAMETER
;
302 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
306 memset(&ifr
, 0, sizeof(struct ifreq
));
307 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
308 if ((ioctl(fd
, SIOCGIFHWADDR
, &ifr
)))
309 ret
= ERROR_INVALID_DATA
;
311 unsigned int addrLen
;
313 switch (ifr
.ifr_hwaddr
.sa_family
)
315 #ifdef ARPHRD_LOOPBACK
316 case ARPHRD_LOOPBACK
:
318 *type
= MIB_IF_TYPE_LOOPBACK
;
324 *type
= MIB_IF_TYPE_ETHERNET
;
330 *type
= MIB_IF_TYPE_FDDI
;
333 #ifdef ARPHRD_IEEE802
334 case ARPHRD_IEEE802
: /* 802.2 Ethernet && Token Ring, guess TR? */
336 *type
= MIB_IF_TYPE_TOKENRING
;
339 #ifdef ARPHRD_IEEE802_TR
340 case ARPHRD_IEEE802_TR
: /* also Token Ring? */
342 *type
= MIB_IF_TYPE_TOKENRING
;
348 *type
= MIB_IF_TYPE_SLIP
;
354 *type
= MIB_IF_TYPE_PPP
;
358 addrLen
= min(MAX_INTERFACE_PHYSADDR
, sizeof(ifr
.ifr_hwaddr
.sa_data
));
359 *type
= MIB_IF_TYPE_OTHER
;
361 if (addrLen
> *len
) {
362 ret
= ERROR_INSUFFICIENT_BUFFER
;
367 memcpy(addr
, ifr
.ifr_hwaddr
.sa_data
, addrLen
);
368 /* zero out remaining bytes for broken implementations */
369 memset(addr
+ addrLen
, 0, *len
- addrLen
);
377 ret
= ERROR_NO_MORE_FILES
;
380 #elif defined (SIOCGARP)
381 DWORD
getInterfacePhysicalByName(const char *name
, PDWORD len
, PBYTE addr
,
387 if (!name
|| !len
|| !addr
|| !type
)
388 return ERROR_INVALID_PARAMETER
;
390 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
392 if (isLoopbackInterface(fd
, name
)) {
393 *type
= MIB_IF_TYPE_LOOPBACK
;
394 memset(addr
, 0, *len
);
400 struct sockaddr_in
*saddr
;
404 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
405 ioctl(fd
, SIOCGIFADDR
, &ifr
);
406 memset(&arp
, 0, sizeof(struct arpreq
));
407 arp
.arp_pa
.sa_family
= AF_INET
;
408 saddr
= (struct sockaddr_in
*)&arp
; /* proto addr is first member */
409 saddr
->sin_family
= AF_INET
;
410 memcpy(&saddr
->sin_addr
.s_addr
, ifr
.ifr_addr
.sa_data
+ 2, sizeof(DWORD
));
411 if ((ioctl(fd
, SIOCGARP
, &arp
)))
412 ret
= ERROR_INVALID_DATA
;
414 /* FIXME: heh: who said it was ethernet? */
415 int addrLen
= ETH_ALEN
;
417 if (addrLen
> *len
) {
418 ret
= ERROR_INSUFFICIENT_BUFFER
;
423 memcpy(addr
, &arp
.arp_ha
.sa_data
[0], addrLen
);
424 /* zero out remaining bytes for broken implementations */
425 memset(addr
+ addrLen
, 0, *len
- addrLen
);
427 *type
= MIB_IF_TYPE_ETHERNET
;
435 ret
= ERROR_NO_MORE_FILES
;
439 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
440 DWORD
getInterfacePhysicalByName(const char *name
, PDWORD len
, PBYTE addr
,
444 struct if_msghdr
*ifm
;
445 struct sockaddr_dl
*sdl
;
448 int mib
[] = { CTL_NET
, AF_ROUTE
, 0, AF_LINK
, NET_RT_IFLIST
, 0 };
452 if (!name
|| !len
|| !addr
|| !type
)
453 return ERROR_INVALID_PARAMETER
;
455 if (sysctl(mib
, 6, NULL
, &mibLen
, NULL
, 0) < 0)
456 return ERROR_NO_MORE_FILES
;
458 buf
= HeapAlloc(GetProcessHeap(), 0, mibLen
);
460 return ERROR_NOT_ENOUGH_MEMORY
;
462 if (sysctl(mib
, 6, buf
, &mibLen
, NULL
, 0) < 0) {
463 HeapFree(GetProcessHeap(), 0, buf
);
464 return ERROR_NO_MORE_FILES
;
467 ret
= ERROR_INVALID_DATA
;
468 for (p
= buf
; !found
&& p
< buf
+ mibLen
; p
+= ifm
->ifm_msglen
) {
469 ifm
= (struct if_msghdr
*)p
;
470 sdl
= (struct sockaddr_dl
*)(ifm
+ 1);
472 if (ifm
->ifm_type
!= RTM_IFINFO
|| (ifm
->ifm_addrs
& RTA_IFP
) == 0)
475 if (sdl
->sdl_family
!= AF_LINK
|| sdl
->sdl_nlen
== 0 ||
476 memcmp(sdl
->sdl_data
, name
, max(sdl
->sdl_nlen
, strlen(name
))) != 0)
480 addrLen
= min(MAX_INTERFACE_PHYSADDR
, sdl
->sdl_alen
);
481 if (addrLen
> *len
) {
482 ret
= ERROR_INSUFFICIENT_BUFFER
;
487 memcpy(addr
, LLADDR(sdl
), addrLen
);
488 /* zero out remaining bytes for broken implementations */
489 memset(addr
+ addrLen
, 0, *len
- addrLen
);
491 #if defined(HAVE_NET_IF_TYPES_H)
492 switch (sdl
->sdl_type
)
495 *type
= MIB_IF_TYPE_ETHERNET
;
498 *type
= MIB_IF_TYPE_FDDI
;
500 case IFT_ISO88024
: /* Token Bus */
501 *type
= MIB_IF_TYPE_TOKENRING
;
503 case IFT_ISO88025
: /* Token Ring */
504 *type
= MIB_IF_TYPE_TOKENRING
;
507 *type
= MIB_IF_TYPE_PPP
;
510 *type
= MIB_IF_TYPE_SLIP
;
513 *type
= MIB_IF_TYPE_LOOPBACK
;
516 *type
= MIB_IF_TYPE_OTHER
;
519 /* default if we don't know */
520 *type
= MIB_IF_TYPE_ETHERNET
;
525 HeapFree(GetProcessHeap(), 0, buf
);
530 DWORD
getInterfacePhysicalByIndex(DWORD index
, PDWORD len
, PBYTE addr
,
533 char nameBuf
[IF_NAMESIZE
];
534 char *name
= getInterfaceNameByIndex(index
, nameBuf
);
537 return getInterfacePhysicalByName(name
, len
, addr
, type
);
539 return ERROR_INVALID_DATA
;
542 static DWORD
getInterfaceMtuByName(const char *name
, PDWORD mtu
)
548 return ERROR_INVALID_PARAMETER
;
550 return ERROR_INVALID_PARAMETER
;
552 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
556 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
557 if ((ioctl(fd
, SIOCGIFMTU
, &ifr
)))
558 ret
= ERROR_INVALID_DATA
;
563 *mtu
= ifr
.ifr_metric
;
570 ret
= ERROR_NO_MORE_FILES
;
574 static DWORD
getInterfaceStatusByName(const char *name
, PDWORD status
)
580 return ERROR_INVALID_PARAMETER
;
582 return ERROR_INVALID_PARAMETER
;
584 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
588 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
589 if ((ioctl(fd
, SIOCGIFFLAGS
, &ifr
)))
590 ret
= ERROR_INVALID_DATA
;
592 if (ifr
.ifr_flags
& IFF_UP
)
593 *status
= MIB_IF_OPER_STATUS_OPERATIONAL
;
595 *status
= MIB_IF_OPER_STATUS_NON_OPERATIONAL
;
601 ret
= ERROR_NO_MORE_FILES
;
605 DWORD
getInterfaceEntryByName(const char *name
, PMIB_IFROW entry
)
607 BYTE addr
[MAX_INTERFACE_PHYSADDR
];
608 DWORD ret
, len
= sizeof(addr
), type
;
611 return ERROR_INVALID_PARAMETER
;
613 return ERROR_INVALID_PARAMETER
;
615 if (getInterfacePhysicalByName(name
, &len
, addr
, &type
) == NO_ERROR
) {
619 memset(entry
, 0, sizeof(MIB_IFROW
));
620 for (assigner
= entry
->wszName
, walker
= name
; *walker
;
621 walker
++, assigner
++)
624 getInterfaceIndexByName(name
, &entry
->dwIndex
);
625 entry
->dwPhysAddrLen
= len
;
626 memcpy(entry
->bPhysAddr
, addr
, len
);
627 memset(entry
->bPhysAddr
+ len
, 0, sizeof(entry
->bPhysAddr
) - len
);
628 entry
->dwType
= type
;
629 /* FIXME: how to calculate real speed? */
630 getInterfaceMtuByName(name
, &entry
->dwMtu
);
631 /* lie, there's no "administratively down" here */
632 entry
->dwAdminStatus
= MIB_IF_ADMIN_STATUS_UP
;
633 getInterfaceStatusByName(name
, &entry
->dwOperStatus
);
634 /* punt on dwLastChange? */
635 entry
->dwDescrLen
= min(strlen(name
), MAX_INTERFACE_DESCRIPTION
- 1);
636 memcpy(entry
->bDescr
, name
, entry
->dwDescrLen
);
637 entry
->bDescr
[entry
->dwDescrLen
] = '\0';
642 ret
= ERROR_INVALID_DATA
;
646 DWORD
getInterfaceEntryByIndex(DWORD index
, PMIB_IFROW entry
)
648 char nameBuf
[IF_NAMESIZE
];
649 char *name
= getInterfaceNameByIndex(index
, nameBuf
);
652 return getInterfaceEntryByName(name
, entry
);
654 return ERROR_INVALID_DATA
;
657 /* Enumerates the IP addresses in the system using SIOCGIFCONF, returning
658 * the count to you in *pcAddresses. It also returns to you the struct ifconf
659 * used by the call to ioctl, so that you may process the addresses further.
660 * Free ifc->ifc_buf using HeapFree.
661 * Returns NO_ERROR on success, something else on failure.
663 static DWORD
enumIPAddresses(PDWORD pcAddresses
, struct ifconf
*ifc
)
668 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
671 DWORD guessedNumAddresses
= 0, numAddresses
= 0;
677 /* there is no way to know the interface count beforehand,
678 so we need to loop again and again upping our max each time
679 until returned < max */
681 HeapFree(GetProcessHeap(), 0, ifc
->ifc_buf
);
682 if (guessedNumAddresses
== 0)
683 guessedNumAddresses
= INITIAL_INTERFACES_ASSUMED
;
685 guessedNumAddresses
*= 2;
686 ifc
->ifc_len
= sizeof(struct ifreq
) * guessedNumAddresses
;
687 ifc
->ifc_buf
= HeapAlloc(GetProcessHeap(), 0, ifc
->ifc_len
);
688 ioctlRet
= ioctl(fd
, SIOCGIFCONF
, ifc
);
689 } while (ioctlRet
== 0 &&
690 ifc
->ifc_len
== (sizeof(struct ifreq
) * guessedNumAddresses
));
693 ifPtr
= ifc
->ifc_buf
;
694 while (ifPtr
&& ifPtr
< ifc
->ifc_buf
+ ifc
->ifc_len
) {
696 ifPtr
+= ifreq_len((struct ifreq
*)ifPtr
);
700 ret
= ERROR_INVALID_PARAMETER
; /* FIXME: map from errno to Win32 */
702 *pcAddresses
= numAddresses
;
705 HeapFree(GetProcessHeap(), 0, ifc
->ifc_buf
);
711 ret
= ERROR_NO_SYSTEM_RESOURCES
;
715 DWORD
getNumIPAddresses(void)
717 DWORD numAddresses
= 0;
720 if (!enumIPAddresses(&numAddresses
, &ifc
))
721 HeapFree(GetProcessHeap(), 0, ifc
.ifc_buf
);
725 DWORD
getIPAddrTable(PMIB_IPADDRTABLE
*ppIpAddrTable
, HANDLE heap
, DWORD flags
)
730 ret
= ERROR_INVALID_PARAMETER
;
733 DWORD numAddresses
= 0;
736 ret
= enumIPAddresses(&numAddresses
, &ifc
);
739 *ppIpAddrTable
= HeapAlloc(heap
, flags
, sizeof(MIB_IPADDRTABLE
) +
740 (numAddresses
- 1) * sizeof(MIB_IPADDRROW
));
741 if (*ppIpAddrTable
) {
746 (*ppIpAddrTable
)->dwNumEntries
= numAddresses
;
748 while (!ret
&& ifPtr
&& ifPtr
< ifc
.ifc_buf
+ ifc
.ifc_len
) {
749 struct ifreq
*ifr
= (struct ifreq
*)ifPtr
;
751 ret
= getInterfaceIndexByName(ifr
->ifr_name
,
752 &(*ppIpAddrTable
)->table
[i
].dwIndex
);
753 memcpy(&(*ppIpAddrTable
)->table
[i
].dwAddr
, ifr
->ifr_addr
.sa_data
+ 2,
755 (*ppIpAddrTable
)->table
[i
].dwMask
=
756 getInterfaceMaskByName(ifr
->ifr_name
);
757 /* the dwBCastAddr member isn't the broadcast address, it indicates
758 * whether the interface uses the 1's broadcast address (1) or the
759 * 0's broadcast address (0).
761 bcast
= getInterfaceBCastAddrByName(ifr
->ifr_name
);
762 (*ppIpAddrTable
)->table
[i
].dwBCastAddr
=
763 (bcast
& (*ppIpAddrTable
)->table
[i
].dwMask
) ? 1 : 0;
764 /* FIXME: hardcoded reasm size, not sure where to get it */
765 (*ppIpAddrTable
)->table
[i
].dwReasmSize
= 65535;
767 (*ppIpAddrTable
)->table
[i
].unused1
= 0;
768 (*ppIpAddrTable
)->table
[i
].wType
= 0;
769 ifPtr
+= ifreq_len(ifr
);
774 ret
= ERROR_OUTOFMEMORY
;
775 HeapFree(GetProcessHeap(), 0, ifc
.ifc_buf
);
781 char *toIPAddressString(unsigned int addr
, char string
[16])
784 struct in_addr iAddr
;
787 /* extra-anal, just to make auditors happy */
788 lstrcpynA(string
, inet_ntoa(iAddr
), 16);