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>
97 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
98 #define ifreq_len(ifr) \
99 max(sizeof(struct ifreq), sizeof((ifr)->ifr_name)+(ifr)->ifr_addr.sa_len)
101 #define ifreq_len(ifr) sizeof(struct ifreq)
109 #define IF_NAMESIZE 16
113 #define INADDR_NONE (~0U)
116 #define INITIAL_INTERFACES_ASSUMED 4
120 static int isLoopbackInterface(int fd, const char *name)
127 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
128 if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0)
129 ret = ifr.ifr_flags & IFF_LOOPBACK;
134 /* The comments say MAX_ADAPTER_NAME is required, but really only IF_NAMESIZE
135 * bytes are necessary.
137 char *getInterfaceNameByIndex(DWORD index, char *name)
139 return if_indextoname(index, name);
142 DWORD getInterfaceIndexByName(const char *name, PDWORD index)
148 return ERROR_INVALID_PARAMETER;
150 return ERROR_INVALID_PARAMETER;
151 idx = if_nametoindex(name);
157 ret = ERROR_INVALID_DATA;
161 BOOL isIfIndexLoopback(ULONG idx)
167 getInterfaceNameByIndex(idx, name);
168 fd = socket(PF_INET, SOCK_DGRAM, 0);
170 ret = isLoopbackInterface(fd, name);
176 DWORD getNumNonLoopbackInterfaces(void)
179 int fd = socket(PF_INET, SOCK_DGRAM, 0);
182 struct if_nameindex *indexes = if_nameindex();
185 struct if_nameindex *p;
187 for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
188 if (!isLoopbackInterface(fd, p->if_name))
190 if_freenameindex(indexes);
198 return numInterfaces;
201 DWORD getNumInterfaces(void)
204 struct if_nameindex *indexes = if_nameindex();
207 struct if_nameindex *p;
209 for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
211 if_freenameindex(indexes);
215 return numInterfaces;
218 InterfaceIndexTable *getInterfaceIndexTable(void)
221 InterfaceIndexTable *ret;
222 struct if_nameindex *indexes = if_nameindex();
225 struct if_nameindex *p;
226 DWORD size = sizeof(InterfaceIndexTable);
228 for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
230 if (numInterfaces > 1)
231 size += (numInterfaces - 1) * sizeof(DWORD);
232 ret = HeapAlloc(GetProcessHeap(), 0, size);
235 for (p = indexes; p && p->if_name; p++)
236 ret->indexes[ret->numIndexes++] = p->if_index;
238 if_freenameindex(indexes);
245 InterfaceIndexTable *getNonLoopbackInterfaceIndexTable(void)
248 InterfaceIndexTable *ret;
249 int fd = socket(PF_INET, SOCK_DGRAM, 0);
252 struct if_nameindex *indexes = if_nameindex();
255 struct if_nameindex *p;
256 DWORD size = sizeof(InterfaceIndexTable);
258 for (p = indexes, numInterfaces = 0; p && p->if_name; p++)
259 if (!isLoopbackInterface(fd, p->if_name))
261 if (numInterfaces > 1)
262 size += (numInterfaces - 1) * sizeof(DWORD);
263 ret = HeapAlloc(GetProcessHeap(), 0, size);
266 for (p = indexes; p && p->if_name; p++)
267 if (!isLoopbackInterface(fd, p->if_name))
268 ret->indexes[ret->numIndexes++] = p->if_index;
270 if_freenameindex(indexes);
281 static DWORD getInterfaceBCastAddrByName(const char *name)
283 DWORD ret = INADDR_ANY;
286 int fd = socket(PF_INET, SOCK_DGRAM, 0);
291 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
292 if (ioctl(fd, SIOCGIFBRDADDR, &ifr) == 0)
293 memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
300 static DWORD getInterfaceMaskByName(const char *name)
302 DWORD ret = INADDR_NONE;
305 int fd = socket(PF_INET, SOCK_DGRAM, 0);
310 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
311 if (ioctl(fd, SIOCGIFNETMASK, &ifr) == 0)
312 memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
319 #if defined (SIOCGIFHWADDR) && defined (HAVE_STRUCT_IFREQ_IFR_HWADDR)
320 static DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
326 if (!name || !len || !addr || !type)
327 return ERROR_INVALID_PARAMETER;
329 fd = socket(PF_INET, SOCK_DGRAM, 0);
333 memset(&ifr, 0, sizeof(struct ifreq));
334 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
335 if ((ioctl(fd, SIOCGIFHWADDR, &ifr)))
336 ret = ERROR_INVALID_DATA;
338 unsigned int addrLen;
340 switch (ifr.ifr_hwaddr.sa_family)
342 #ifdef ARPHRD_LOOPBACK
343 case ARPHRD_LOOPBACK:
345 *type = MIB_IF_TYPE_LOOPBACK;
351 *type = MIB_IF_TYPE_ETHERNET;
357 *type = MIB_IF_TYPE_FDDI;
360 #ifdef ARPHRD_IEEE802
361 case ARPHRD_IEEE802: /* 802.2 Ethernet && Token Ring, guess TR? */
363 *type = MIB_IF_TYPE_TOKENRING;
366 #ifdef ARPHRD_IEEE802_TR
367 case ARPHRD_IEEE802_TR: /* also Token Ring? */
369 *type = MIB_IF_TYPE_TOKENRING;
375 *type = MIB_IF_TYPE_SLIP;
381 *type = MIB_IF_TYPE_PPP;
385 addrLen = min(MAX_INTERFACE_PHYSADDR, sizeof(ifr.ifr_hwaddr.sa_data));
386 *type = MIB_IF_TYPE_OTHER;
388 if (addrLen > *len) {
389 ret = ERROR_INSUFFICIENT_BUFFER;
394 memcpy(addr, ifr.ifr_hwaddr.sa_data, addrLen);
395 /* zero out remaining bytes for broken implementations */
396 memset(addr + addrLen, 0, *len - addrLen);
404 ret = ERROR_NO_MORE_FILES;
407 #elif defined (SIOCGARP)
408 static DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
414 if (!name || !len || !addr || !type)
415 return ERROR_INVALID_PARAMETER;
417 fd = socket(PF_INET, SOCK_DGRAM, 0);
419 if (isLoopbackInterface(fd, name)) {
420 *type = MIB_IF_TYPE_LOOPBACK;
421 memset(addr, 0, *len);
427 struct sockaddr_in *saddr;
431 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
432 ioctl(fd, SIOCGIFADDR, &ifr);
433 memset(&arp, 0, sizeof(struct arpreq));
434 arp.arp_pa.sa_family = AF_INET;
435 saddr = (struct sockaddr_in *)&arp; /* proto addr is first member */
436 saddr->sin_family = AF_INET;
437 memcpy(&saddr->sin_addr.s_addr, ifr.ifr_addr.sa_data + 2, sizeof(DWORD));
438 if ((ioctl(fd, SIOCGARP, &arp)))
439 ret = ERROR_INVALID_DATA;
441 /* FIXME: heh: who said it was ethernet? */
442 int addrLen = ETH_ALEN;
444 if (addrLen > *len) {
445 ret = ERROR_INSUFFICIENT_BUFFER;
450 memcpy(addr, &arp.arp_ha.sa_data[0], addrLen);
451 /* zero out remaining bytes for broken implementations */
452 memset(addr + addrLen, 0, *len - addrLen);
454 *type = MIB_IF_TYPE_ETHERNET;
462 ret = ERROR_NO_MORE_FILES;
466 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
467 static DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr,
471 struct if_msghdr *ifm;
472 struct sockaddr_dl *sdl;
475 int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
479 if (!name || !len || !addr || !type)
480 return ERROR_INVALID_PARAMETER;
482 if (sysctl(mib, 6, NULL, &mibLen, NULL, 0) < 0)
483 return ERROR_NO_MORE_FILES;
485 buf = HeapAlloc(GetProcessHeap(), 0, mibLen);
487 return ERROR_NOT_ENOUGH_MEMORY;
489 if (sysctl(mib, 6, buf, &mibLen, NULL, 0) < 0) {
490 HeapFree(GetProcessHeap(), 0, buf);
491 return ERROR_NO_MORE_FILES;
494 ret = ERROR_INVALID_DATA;
495 for (p = buf; !found && p < buf + mibLen; p += ifm->ifm_msglen) {
496 ifm = (struct if_msghdr *)p;
497 sdl = (struct sockaddr_dl *)(ifm + 1);
499 if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0)
502 if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
503 memcmp(sdl->sdl_data, name, max(sdl->sdl_nlen, strlen(name))) != 0)
507 addrLen = min(MAX_INTERFACE_PHYSADDR, sdl->sdl_alen);
508 if (addrLen > *len) {
509 ret = ERROR_INSUFFICIENT_BUFFER;
514 memcpy(addr, LLADDR(sdl), addrLen);
515 /* zero out remaining bytes for broken implementations */
516 memset(addr + addrLen, 0, *len - addrLen);
518 #if defined(HAVE_NET_IF_TYPES_H)
519 switch (sdl->sdl_type)
522 *type = MIB_IF_TYPE_ETHERNET;
525 *type = MIB_IF_TYPE_FDDI;
527 case IFT_ISO88024: /* Token Bus */
528 *type = MIB_IF_TYPE_TOKENRING;
530 case IFT_ISO88025: /* Token Ring */
531 *type = MIB_IF_TYPE_TOKENRING;
534 *type = MIB_IF_TYPE_PPP;
537 *type = MIB_IF_TYPE_SLIP;
540 *type = MIB_IF_TYPE_LOOPBACK;
543 *type = MIB_IF_TYPE_OTHER;
546 /* default if we don't know */
547 *type = MIB_IF_TYPE_ETHERNET;
552 HeapFree(GetProcessHeap(), 0, buf);
557 DWORD getInterfacePhysicalByIndex(DWORD index, PDWORD len, PBYTE addr,
560 char nameBuf[IF_NAMESIZE];
561 char *name = getInterfaceNameByIndex(index, nameBuf);
564 return getInterfacePhysicalByName(name, len, addr, type);
566 return ERROR_INVALID_DATA;
569 DWORD getInterfaceMtuByName(const char *name, PDWORD mtu)
575 return ERROR_INVALID_PARAMETER;
577 return ERROR_INVALID_PARAMETER;
579 fd = socket(PF_INET, SOCK_DGRAM, 0);
583 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
584 if ((ioctl(fd, SIOCGIFMTU, &ifr)))
585 ret = ERROR_INVALID_DATA;
590 *mtu = ifr.ifr_metric;
597 ret = ERROR_NO_MORE_FILES;
601 DWORD getInterfaceStatusByName(const char *name, PDWORD status)
607 return ERROR_INVALID_PARAMETER;
609 return ERROR_INVALID_PARAMETER;
611 fd = socket(PF_INET, SOCK_DGRAM, 0);
615 lstrcpynA(ifr.ifr_name, name, IFNAMSIZ);
616 if ((ioctl(fd, SIOCGIFFLAGS, &ifr)))
617 ret = ERROR_INVALID_DATA;
619 if (ifr.ifr_flags & IFF_UP)
620 *status = MIB_IF_OPER_STATUS_OPERATIONAL;
622 *status = MIB_IF_OPER_STATUS_NON_OPERATIONAL;
628 ret = ERROR_NO_MORE_FILES;
632 DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry)
634 BYTE addr[MAX_INTERFACE_PHYSADDR];
635 DWORD ret, len = sizeof(addr), type;
638 return ERROR_INVALID_PARAMETER;
640 return ERROR_INVALID_PARAMETER;
642 if (getInterfacePhysicalByName(name, &len, addr, &type) == NO_ERROR) {
646 memset(entry, 0, sizeof(MIB_IFROW));
647 for (assigner = entry->wszName, walker = name; *walker;
648 walker++, assigner++)
651 getInterfaceIndexByName(name, &entry->dwIndex);
652 entry->dwPhysAddrLen = len;
653 memcpy(entry->bPhysAddr, addr, len);
654 memset(entry->bPhysAddr + len, 0, sizeof(entry->bPhysAddr) - len);
655 entry->dwType = type;
656 /* FIXME: how to calculate real speed? */
657 getInterfaceMtuByName(name, &entry->dwMtu);
658 /* lie, there's no "administratively down" here */
659 entry->dwAdminStatus = MIB_IF_ADMIN_STATUS_UP;
660 getInterfaceStatusByName(name, &entry->dwOperStatus);
661 /* punt on dwLastChange? */
662 entry->dwDescrLen = min(strlen(name), MAX_INTERFACE_DESCRIPTION - 1);
663 memcpy(entry->bDescr, name, entry->dwDescrLen);
664 entry->bDescr[entry->dwDescrLen] = '\0';
669 ret = ERROR_INVALID_DATA;
673 static DWORD getIPAddrRowByName(PMIB_IPADDRROW ipAddrRow, const char *ifName,
674 const struct sockaddr *sa)
678 ret = getInterfaceIndexByName(ifName, &ipAddrRow->dwIndex);
679 memcpy(&ipAddrRow->dwAddr, sa->sa_data + 2, sizeof(DWORD));
680 ipAddrRow->dwMask = getInterfaceMaskByName(ifName);
681 /* the dwBCastAddr member isn't the broadcast address, it indicates whether
682 * the interface uses the 1's broadcast address (1) or the 0's broadcast
685 bcast = getInterfaceBCastAddrByName(ifName);
686 ipAddrRow->dwBCastAddr = (bcast & ipAddrRow->dwMask) ? 1 : 0;
687 /* FIXME: hardcoded reasm size, not sure where to get it */
688 ipAddrRow->dwReasmSize = 65535;
689 ipAddrRow->unused1 = 0;
690 ipAddrRow->wType = 0;
694 #ifdef HAVE_IFADDRS_H
696 /* Counts the IPv4 addresses in the system using the return value from
697 * getifaddrs, returning the count.
699 static DWORD countIPv4Addresses(struct ifaddrs *ifa)
701 DWORD numAddresses = 0;
703 for (; ifa; ifa = ifa->ifa_next)
704 if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET)
709 DWORD getNumIPAddresses(void)
711 DWORD numAddresses = 0;
714 if (!getifaddrs(&ifa))
716 numAddresses = countIPv4Addresses(ifa);
722 DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags)
727 ret = ERROR_INVALID_PARAMETER;
732 if (!getifaddrs(&ifa))
734 DWORD size = sizeof(MIB_IPADDRTABLE);
735 DWORD numAddresses = countIPv4Addresses(ifa);
737 if (numAddresses > 1)
738 size += (numAddresses - 1) * sizeof(MIB_IPADDRROW);
739 *ppIpAddrTable = HeapAlloc(heap, flags, size);
746 (*ppIpAddrTable)->dwNumEntries = numAddresses;
747 for (ifp = ifa; !ret && ifp; ifp = ifp->ifa_next)
749 if (!ifp->ifa_addr || ifp->ifa_addr->sa_family != AF_INET)
752 ret = getIPAddrRowByName(&(*ppIpAddrTable)->table[i], ifp->ifa_name,
758 ret = ERROR_OUTOFMEMORY;
762 ret = ERROR_INVALID_PARAMETER;
769 /* Enumerates the IP addresses in the system using SIOCGIFCONF, returning
770 * the count to you in *pcAddresses. It also returns to you the struct ifconf
771 * used by the call to ioctl, so that you may process the addresses further.
772 * Free ifc->ifc_buf using HeapFree.
773 * Returns NO_ERROR on success, something else on failure.
775 static DWORD enumIPAddresses(PDWORD pcAddresses, struct ifconf *ifc)
780 fd = socket(PF_INET, SOCK_DGRAM, 0);
783 DWORD guessedNumAddresses = 0, numAddresses = 0;
790 /* there is no way to know the interface count beforehand,
791 so we need to loop again and again upping our max each time
792 until returned is constant across 2 calls */
794 lastlen = ifc->ifc_len;
795 HeapFree(GetProcessHeap(), 0, ifc->ifc_buf);
796 if (guessedNumAddresses == 0)
797 guessedNumAddresses = INITIAL_INTERFACES_ASSUMED;
799 guessedNumAddresses *= 2;
800 ifc->ifc_len = sizeof(struct ifreq) * guessedNumAddresses;
801 ifc->ifc_buf = HeapAlloc(GetProcessHeap(), 0, ifc->ifc_len);
802 ioctlRet = ioctl(fd, SIOCGIFCONF, ifc);
803 } while ((ioctlRet == 0) && (ifc->ifc_len != lastlen));
806 ifPtr = ifc->ifc_buf;
807 while (ifPtr && ifPtr < ifc->ifc_buf + ifc->ifc_len) {
808 struct ifreq *ifr = (struct ifreq *)ifPtr;
810 if (ifr->ifr_addr.sa_family == AF_INET)
813 ifPtr += ifreq_len((struct ifreq *)ifPtr);
817 ret = ERROR_INVALID_PARAMETER; /* FIXME: map from errno to Win32 */
819 *pcAddresses = numAddresses;
822 HeapFree(GetProcessHeap(), 0, ifc->ifc_buf);
828 ret = ERROR_NO_SYSTEM_RESOURCES;
832 DWORD getNumIPAddresses(void)
834 DWORD numAddresses = 0;
837 if (!enumIPAddresses(&numAddresses, &ifc))
838 HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
842 DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags)
847 ret = ERROR_INVALID_PARAMETER;
850 DWORD numAddresses = 0;
853 ret = enumIPAddresses(&numAddresses, &ifc);
856 DWORD size = sizeof(MIB_IPADDRTABLE);
858 if (numAddresses > 1)
859 size += (numAddresses - 1) * sizeof(MIB_IPADDRROW);
860 *ppIpAddrTable = HeapAlloc(heap, flags, size);
861 if (*ppIpAddrTable) {
866 (*ppIpAddrTable)->dwNumEntries = numAddresses;
868 while (!ret && ifPtr && ifPtr < ifc.ifc_buf + ifc.ifc_len) {
869 struct ifreq *ifr = (struct ifreq *)ifPtr;
871 ifPtr += ifreq_len(ifr);
873 if (ifr->ifr_addr.sa_family != AF_INET)
876 ret = getIPAddrRowByName(&(*ppIpAddrTable)->table[i], ifr->ifr_name,
882 ret = ERROR_OUTOFMEMORY;
883 HeapFree(GetProcessHeap(), 0, ifc.ifc_buf);
891 #ifdef HAVE_IFADDRS_H
892 ULONG v6addressesFromIndex(DWORD index, SOCKET_ADDRESS **addrs, ULONG *num_addrs)
897 if (!getifaddrs(&ifa))
903 getInterfaceNameByIndex(index, name);
904 for (p = ifa, n = 0; p; p = p->ifa_next)
905 if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET6 &&
906 !strcmp(name, p->ifa_name))
910 *addrs = HeapAlloc(GetProcessHeap(), 0, n * (sizeof(SOCKET_ADDRESS) +
911 sizeof(struct WS_sockaddr_in6)));
914 struct WS_sockaddr_in6 *next_addr = (struct WS_sockaddr_in6 *)(
915 (BYTE *)*addrs + n * sizeof(SOCKET_ADDRESS));
917 for (p = ifa, n = 0; p; p = p->ifa_next)
919 if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET6 &&
920 !strcmp(name, p->ifa_name))
922 struct sockaddr_in6 *addr = (struct sockaddr_in6 *)p->ifa_addr;
924 next_addr->sin6_family = WS_AF_INET6;
925 next_addr->sin6_port = addr->sin6_port;
926 next_addr->sin6_flowinfo = addr->sin6_flowinfo;
927 memcpy(&next_addr->sin6_addr, &addr->sin6_addr,
928 sizeof(next_addr->sin6_addr));
929 next_addr->sin6_scope_id = addr->sin6_scope_id;
930 (*addrs)[n].lpSockaddr = (LPSOCKADDR)next_addr;
931 (*addrs)[n].iSockaddrLength = sizeof(struct WS_sockaddr_in6);
940 ret = ERROR_OUTOFMEMORY;
955 ULONG v6addressesFromIndex(DWORD index, SOCKET_ADDRESS **addrs, ULONG *num_addrs)
959 return ERROR_SUCCESS;
963 char *toIPAddressString(unsigned int addr, char string[16])
966 struct in_addr iAddr;
969 /* extra-anal, just to make auditors happy */
970 lstrcpynA(string, inet_ntoa(iAddr), 16);