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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 Mac OS X)
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>
42 #ifdef HAVE_SYS_PARAM_H
43 #include <sys/param.h>
46 #ifdef HAVE_SYS_SOCKET_H
47 #include <sys/socket.h>
50 #ifdef HAVE_NETINET_IN_H
51 #include <netinet/in.h>
54 #ifdef HAVE_ARPA_INET_H
55 #include <arpa/inet.h>
62 #ifdef HAVE_NET_IF_ARP_H
63 #include <net/if_arp.h>
66 #ifdef HAVE_NET_ROUTE_H
67 #include <net/route.h>
70 #ifdef HAVE_SYS_IOCTL_H
71 #include <sys/ioctl.h>
74 #ifdef HAVE_SYS_SYSCTL_H
75 #include <sys/sysctl.h>
78 #ifdef HAVE_SYS_SOCKIO_H
79 #include <sys/sockio.h>
82 #ifdef HAVE_NET_IF_DL_H
83 #include <net/if_dl.h>
86 #ifdef HAVE_NET_IF_TYPES_H
87 #include <net/if_types.h>
92 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
93 #define ifreq_len(ifr) \
94 max(sizeof(struct ifreq), sizeof((ifr)->ifr_name)+(ifr)->ifr_addr.sa_len)
96 #define ifreq_len(ifr) sizeof(struct ifreq)
104 #define IF_NAMESIZE 16
108 #define INADDR_NONE (~0U)
111 #define INITIAL_INTERFACES_ASSUMED 4
113 #define INDEX_IS_LOOPBACK 0x00800000
117 static int isLoopbackInterface(int fd, const char *name)
124 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
125 if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0)
126 ret = ifr.ifr_flags & IFF_LOOPBACK;
131 /* The comments say MAX_ADAPTER_NAME is required, but really only IF_NAMESIZE
132 * bytes are necessary.
134 char *getInterfaceNameByIndex(DWORD index, char *name)
136 return if_indextoname(index, name);
139 DWORD getInterfaceIndexByName(const char *name, PDWORD index)
145 return ERROR_INVALID_PARAMETER;
147 return ERROR_INVALID_PARAMETER;
148 idx = if_nametoindex(name);
154 ret = ERROR_INVALID_DATA;
158 DWORD getNumNonLoopbackInterfaces(void)
161 int fd = socket(PF_INET, SOCK_DGRAM, 0);
164 struct if_nameindex *indexes = if_nameindex();
167 struct if_nameindex *p;
169 for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
170 if (!isLoopbackInterface(fd, p->if_name))
172 if_freenameindex(indexes);
180 return numInterfaces;
183 DWORD getNumInterfaces(void)
186 struct if_nameindex *indexes = if_nameindex();
189 struct if_nameindex *p;
191 for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
193 if_freenameindex(indexes);
197 return numInterfaces;
200 InterfaceIndexTable *getInterfaceIndexTable(void)
203 InterfaceIndexTable *ret;
204 struct if_nameindex *indexes = if_nameindex();
207 struct if_nameindex *p;
208 DWORD size = sizeof(InterfaceIndexTable);
210 for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
212 if (numInterfaces > 1)
213 size += (numInterfaces - 1) * sizeof(DWORD);
214 ret = HeapAlloc(GetProcessHeap(), 0, size);
217 for (p = indexes; p && p->if_name; p++)
218 ret->indexes[ret->numIndexes++] = p->if_index;
220 if_freenameindex(indexes);
227 InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void)
230 InterfaceIndexTable *ret;
231 int fd = socket(PF_INET, SOCK_DGRAM, 0);
234 struct if_nameindex *indexes = if_nameindex();
237 struct if_nameindex *p;
238 DWORD size = sizeof(InterfaceIndexTable);
240 for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
241 if (!isLoopbackInterface(fd, p->if_name))
243 if (numInterfaces > 1)
244 size += (numInterfaces - 1) * sizeof(DWORD);
245 ret = HeapAlloc(GetProcessHeap(), 0, size);
248 for (p = indexes; p && p->if_name; p++)
249 if (!isLoopbackInterface(fd, p->if_name))
250 ret->indexes[ret->numIndexes++] = p->if_index;
252 if_freenameindex(indexes);
263 static DWORD getInterfaceBCastAddrByName(const char *name)
265 DWORD ret = INADDR_ANY;
268 int fd = socket(PF_INET, SOCK_DGRAM, 0);
273 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
274 if (ioctl(fd, SIOCGIFBRDADDR, &ifr) == 0)
275 memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
282 static DWORD getInterfaceMaskByName(const char *name)
284 DWORD ret = INADDR_NONE;
287 int fd = socket(PF_INET, SOCK_DGRAM, 0);
292 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
293 if (ioctl(fd, SIOCGIFNETMASK, &ifr) == 0)
294 memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
301 #if defined (SIOCGIFHWADDR)
302 static DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
308 if (!name || !len || !addr || !type)
309 return ERROR_INVALID_PARAMETER;
311 fd = socket(PF_INET, SOCK_DGRAM, 0);
315 memset(&ifr, 0, sizeof(struct ifreq));
316 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
317 if ((ioctl(fd, SIOCGIFHWADDR, &ifr)))
318 ret = ERROR_INVALID_DATA;
320 unsigned int addrLen;
322 switch (ifr.ifr_hwaddr.sa_family)
324 #ifdef ARPHRD_LOOPBACK
325 case ARPHRD_LOOPBACK:
327 *type = MIB_IF_TYPE_LOOPBACK;
333 *type = MIB_IF_TYPE_ETHERNET;
339 *type = MIB_IF_TYPE_FDDI;
342 #ifdef ARPHRD_IEEE802
343 case ARPHRD_IEEE802: /* 802.2 Ethernet && Token Ring, guess TR? */
345 *type = MIB_IF_TYPE_TOKENRING;
348 #ifdef ARPHRD_IEEE802_TR
349 case ARPHRD_IEEE802_TR: /* also Token Ring? */
351 *type = MIB_IF_TYPE_TOKENRING;
357 *type = MIB_IF_TYPE_SLIP;
363 *type = MIB_IF_TYPE_PPP;
367 addrLen = min(MAX_INTERFACE_PHYSADDR, sizeof(ifr.ifr_hwaddr.sa_data));
368 *type = MIB_IF_TYPE_OTHER;
370 if (addrLen > *len) {
371 ret = ERROR_INSUFFICIENT_BUFFER;
376 memcpy(addr, ifr.ifr_hwaddr.sa_data, addrLen);
377 /* zero out remaining bytes for broken implementations */
378 memset(addr + addrLen, 0, *len - addrLen);
386 ret = ERROR_NO_MORE_FILES;
389 #elif defined (SIOCGARP)
390 static DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
396 if (!name || !len || !addr || !type)
397 return ERROR_INVALID_PARAMETER;
399 fd = socket(PF_INET, SOCK_DGRAM, 0);
401 if (isLoopbackInterface(fd, name)) {
402 *type = MIB_IF_TYPE_LOOPBACK;
403 memset(addr, 0, *len);
409 struct sockaddr_in *saddr;
413 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
414 ioctl(fd, SIOCGIFADDR, &ifr);
415 memset(&arp, 0, sizeof(struct arpreq));
416 arp.arp_pa.sa_family = AF_INET;
417 saddr = (struct sockaddr_in *)&arp; /* proto addr is first member */
418 saddr->sin_family = AF_INET;
419 memcpy(&saddr->sin_addr.s_addr, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
420 if ((ioctl(fd, SIOCGARP, &arp)))
421 ret = ERROR_INVALID_DATA;
423 /* FIXME: heh: who said it was ethernet? */
424 int addrLen = ETH_ALEN;
426 if (addrLen > *len) {
427 ret = ERROR_INSUFFICIENT_BUFFER;
432 memcpy(addr, &arp.arp_ha.sa_data[0], addrLen);
433 /* zero out remaining bytes for broken implementations */
434 memset(addr + addrLen, 0, *len - addrLen);
436 *type = MIB_IF_TYPE_ETHERNET;
444 ret = ERROR_NO_MORE_FILES;
448 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
449 static DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
453 struct if_msghdr *ifm;
454 struct sockaddr_dl *sdl;
457 int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
461 if (!name || !len || !addr || !type)
462 return ERROR_INVALID_PARAMETER;
464 if (sysctl(mib, 6, NULL, &mibLen, NULL, 0) < 0)
465 return ERROR_NO_MORE_FILES;
467 buf = HeapAlloc(GetProcessHeap(), 0, mibLen);
469 return ERROR_NOT_ENOUGH_MEMORY;
471 if (sysctl(mib, 6, buf, &mibLen, NULL, 0) < 0) {
472 HeapFree(GetProcessHeap(), 0, buf);
473 return ERROR_NO_MORE_FILES;
476 ret = ERROR_INVALID_DATA;
477 for (p = buf; !found && p < buf + mibLen; p += ifm->ifm_msglen) {
478 ifm = (struct if_msghdr *)p;
479 sdl = (struct sockaddr_dl *)(ifm + 1);
481 if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0)
484 if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
485 memcmp(sdl->sdl_data, name, max(sdl->sdl_nlen, strlen(name))) != 0)
489 addrLen = min(MAX_INTERFACE_PHYSADDR, sdl->sdl_alen);
490 if (addrLen > *len) {
491 ret = ERROR_INSUFFICIENT_BUFFER;
496 memcpy(addr, LLADDR(sdl), addrLen);
497 /* zero out remaining bytes for broken implementations */
498 memset(addr + addrLen, 0, *len - addrLen);
500 #if defined(HAVE_NET_IF_TYPES_H)
501 switch (sdl->sdl_type)
504 *type = MIB_IF_TYPE_ETHERNET;
507 *type = MIB_IF_TYPE_FDDI;
509 case IFT_ISO88024: /* Token Bus */
510 *type = MIB_IF_TYPE_TOKENRING;
512 case IFT_ISO88025: /* Token Ring */
513 *type = MIB_IF_TYPE_TOKENRING;
516 *type = MIB_IF_TYPE_PPP;
519 *type = MIB_IF_TYPE_SLIP;
522 *type = MIB_IF_TYPE_LOOPBACK;
525 *type = MIB_IF_TYPE_OTHER;
528 /* default if we don't know */
529 *type = MIB_IF_TYPE_ETHERNET;
534 HeapFree(GetProcessHeap(), 0, buf);
539 DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr,
542 char nameBuf[IF_NAMESIZE];
543 char *name = getInterfaceNameByIndex(index, nameBuf);
546 return getInterfacePhysicalByName(name, len, addr, type);
548 return ERROR_INVALID_DATA;
551 DWORD getInterfaceMtuByName(const char *name, PDWORD mtu)
557 return ERROR_INVALID_PARAMETER;
559 return ERROR_INVALID_PARAMETER;
561 fd = socket(PF_INET, SOCK_DGRAM, 0);
565 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
566 if ((ioctl(fd, SIOCGIFMTU, &ifr)))
567 ret = ERROR_INVALID_DATA;
572 *mtu = ifr.ifr_metric;
579 ret = ERROR_NO_MORE_FILES;
583 DWORD getInterfaceStatusByName(const char *name, PDWORD status)
589 return ERROR_INVALID_PARAMETER;
591 return ERROR_INVALID_PARAMETER;
593 fd = socket(PF_INET, SOCK_DGRAM, 0);
597 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
598 if ((ioctl(fd, SIOCGIFFLAGS, &ifr)))
599 ret = ERROR_INVALID_DATA;
601 if (ifr.ifr_flags & IFF_UP)
602 *status = MIB_IF_OPER_STATUS_OPERATIONAL;
604 *status = MIB_IF_OPER_STATUS_NON_OPERATIONAL;
610 ret = ERROR_NO_MORE_FILES;
614 DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry)
616 BYTE addr[MAX_INTERFACE_PHYSADDR];
617 DWORD ret, len = sizeof(addr), type;
620 return ERROR_INVALID_PARAMETER;
622 return ERROR_INVALID_PARAMETER;
624 if (getInterfacePhysicalByName(name, &len, addr, &type) == NO_ERROR) {
628 memset(entry, 0, sizeof(MIB_IFROW));
629 for (assigner = entry->wszName, walker = name; *walker;
630 walker++, assigner++)
633 getInterfaceIndexByName(name, &entry->dwIndex);
634 entry->dwPhysAddrLen = len;
635 memcpy(entry->bPhysAddr, addr, len);
636 memset(entry->bPhysAddr + len, 0, sizeof(entry->bPhysAddr) - len);
637 entry->dwType = type;
638 /* FIXME: how to calculate real speed? */
639 getInterfaceMtuByName(name, &entry->dwMtu);
640 /* lie, there's no "administratively down" here */
641 entry->dwAdminStatus = MIB_IF_ADMIN_STATUS_UP;
642 getInterfaceStatusByName(name, &entry->dwOperStatus);
643 /* punt on dwLastChange? */
644 entry->dwDescrLen = min(strlen(name), MAX_INTERFACE_DESCRIPTION - 1);
645 memcpy(entry->bDescr, name, entry->dwDescrLen);
646 entry->bDescr[entry->dwDescrLen] = '\0';
651 ret = ERROR_INVALID_DATA;
655 /* Enumerates the IP addresses in the system using SIOCGIFCONF, returning
656 * the count to you in *pcAddresses. It also returns to you the struct ifconf
657 * used by the call to ioctl, so that you may process the addresses further.
658 * Free ifc->ifc_buf using HeapFree.
659 * Returns NO_ERROR on success, something else on failure.
661 static DWORD enumIPAddresses(PDWORD pcAddresses, struct ifconf *ifc)
666 fd = socket(PF_INET, SOCK_DGRAM, 0);
669 DWORD guessedNumAddresses = 0, numAddresses = 0;
676 /* there is no way to know the interface count beforehand,
677 so we need to loop again and again upping our max each time
678 until returned is constant across 2 calls */
680 lastlen = ifc->ifc_len;
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) && (ifc->ifc_len != lastlen));
692 ifPtr = ifc->ifc_buf;
693 while (ifPtr && ifPtr < ifc->ifc_buf + ifc->ifc_len) {
694 struct ifreq *ifr = (struct ifreq *)ifPtr;
696 if (ifr->ifr_addr.sa_family == AF_INET)
699 ifPtr += ifreq_len((struct ifreq *)ifPtr);
703 ret = ERROR_INVALID_PARAMETER; /* FIXME: map from errno to Win32 */
705 *pcAddresses = numAddresses;
708 HeapFree(GetProcessHeap(), 0, ifc->ifc_buf);
714 ret = ERROR_NO_SYSTEM_RESOURCES;
718 DWORD getNumIPAddresses(void)
720 DWORD numAddresses = 0;
723 if (!enumIPAddresses(&numAddresses, &ifc))
724 HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
728 DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags)
733 ret = ERROR_INVALID_PARAMETER;
736 DWORD numAddresses = 0;
739 ret = enumIPAddresses(&numAddresses, &ifc);
742 DWORD size = sizeof(MIB_IPADDRTABLE);
744 if (numAddresses > 1)
745 size += (numAddresses - 1) * sizeof(MIB_IPADDRROW);
746 *ppIpAddrTable = HeapAlloc(heap, flags, size);
747 if (*ppIpAddrTable) {
752 (*ppIpAddrTable)->dwNumEntries = numAddresses;
754 while (!ret && ifPtr && ifPtr < ifc.ifc_buf + ifc.ifc_len) {
755 struct ifreq *ifr = (struct ifreq *)ifPtr;
757 ifPtr += ifreq_len(ifr);
759 if (ifr->ifr_addr.sa_family != AF_INET)
762 ret = getInterfaceIndexByName(ifr->ifr_name,
763 &(*ppIpAddrTable)->table[i].dwIndex);
764 memcpy(&(*ppIpAddrTable)->table[i].dwAddr, ifr->ifr_addr.sa_data + 2,
766 (*ppIpAddrTable)->table[i].dwMask =
767 getInterfaceMaskByName(ifr->ifr_name);
768 /* the dwBCastAddr member isn't the broadcast address, it indicates
769 * whether the interface uses the 1's broadcast address (1) or the
770 * 0's broadcast address (0).
772 bcast = getInterfaceBCastAddrByName(ifr->ifr_name);
773 (*ppIpAddrTable)->table[i].dwBCastAddr =
774 (bcast & (*ppIpAddrTable)->table[i].dwMask) ? 1 : 0;
775 /* FIXME: hardcoded reasm size, not sure where to get it */
776 (*ppIpAddrTable)->table[i].dwReasmSize = 65535;
778 (*ppIpAddrTable)->table[i].unused1 = 0;
779 (*ppIpAddrTable)->table[i].wType = 0;
784 ret = ERROR_OUTOFMEMORY;
785 HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
791 char *toIPAddressString(unsigned int addr, char string[16])
794 struct in_addr iAddr;
797 /* extra-anal, just to make auditors happy */
798 lstrcpynA(string, inet_ntoa(iAddr), 16);