From 08765c0850ee17f999734cb42ba7cd2a616c89cc Mon Sep 17 00:00:00 2001 From: Fabrizio Bertocci Date: Mon, 21 Jun 2021 23:31:20 +0200 Subject: [PATCH] Added initial implementation of SIOCGIFCONFIG, SIOCGIFADDR, SIOCGIFFLAGS --- libc/calls/ioctl-siocgifconf-nt.c | 349 +++++++++++++++++++++--------- libc/calls/ioctl-siocgifconf.c | 9 +- libc/sock/socket-nt.c | 1 + 3 files changed, 253 insertions(+), 106 deletions(-) diff --git a/libc/calls/ioctl-siocgifconf-nt.c b/libc/calls/ioctl-siocgifconf-nt.c index ba9b6f8c4..1eb6a94fe 100644 --- a/libc/calls/ioctl-siocgifconf-nt.c +++ b/libc/calls/ioctl-siocgifconf-nt.c @@ -23,95 +23,21 @@ #include "libc/sock/sock.h" #include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" +#include "libc/sysv/consts/iff.h" #include "libc/nt/winsock.h" #include "libc/nt/errors.h" +#include "libc/nt/iphlpapi.h" +#include "libc/nt/struct/ipAdapterAddress.h" +#include "libc/nt/windows.h" /* Needed for WideCharToMultiByte */ +#include "libc/bits/weaken.h" +#include "libc/assert.h" // TODO: Remove me -#include "libc/bits/weaken.h" #include "libc/stdio/stdio.h" #define PRINTF weaken(printf) #define MAX_INTERFACES 32 -#if 1 - -#define GAA_FLAG_SKIP_UNICAST 0x0001 -#define GAA_FLAG_SKIP_ANYCAST 0x0002 -#define GAA_FLAG_SKIP_MULTICAST 0x0004 -#define GAA_FLAG_SKIP_DNS_SERVER 0x0008 -#define GAA_FLAG_INCLUDE_PREFIX 0x0010 -#define GAA_FLAG_SKIP_FRIENDLY_NAME 0x0020 -#define GAA_FLAG_INCLUDE_WINS_INFO 0x0040 -#define GAA_FLAG_INCLUDE_GATEWAYS 0x0080 -#define GAA_FLAG_INCLUDE_ALL_INTERFACES 0x0100 -#define GAA_FLAG_INCLUDE_ALL_COMPARTMENTS 0x0200 -#define GAA_FLAG_INCLUDE_TUNNEL_BINDINGORDER 0x0400 -#define GAA_FLAG_SKIP_DNS_INFO 0x0800 - -typedef struct _IP_ADAPTER_ADDRESSES_XP { - union { - uint64_t Alignment; - struct { - uint32_t Length; - uint32_t IfIndex; - }; - }; - struct _IP_ADAPTER_ADDRESSES_XP *Next; - char * AdapterName; - void * FirstUnicastAddress; - void * FirstAnycastAddress; - void * FirstMulticastAddress; - void * FirstDnsServerAddress; - wchar_t *DnsSuffix; - wchar_t *Description; - wchar_t *FriendlyName; - /* Filler */ -} IP_ADAPTER_ADDRESSES_XP, *PIP_ADAPTER_ADDRESSES_XP; - -extern unsigned long GetAdaptersAddresses( - uint32_t Family, - uint32_t Flags, - void * Reserved, - char *AdapterAddresses, - uint32_t *SizePointer); - -textwindows int ioctl_siocgifconf_nt(int fd, struct ifconf *ifc) { - uint32_t rv, size; - char * adapter_addresses; - struct _IP_ADAPTER_ADDRESSES_XP *aa; - - PRINTF("FABDEBUG> ioctl(SIOCGIFCONF) for Windows... printf=%p, GetAdaptersAddresses=%p\n", weaken(printf), weaken(GetAdaptersAddresses)); - if (!weaken(GetAdaptersAddresses)) { - PRINTF("FABDEBUG> NULL GetAdaptersAddresses\n"); - return enomem(); - } - rv = weaken(GetAdaptersAddresses)(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &size); - if (rv != kNtErrorBufferOverflow) { - PRINTF("FABDEBUG> GetAdaptersAddresses failed %d\n", WSAGetLastError()); - return ebadf(); - } - PRINTF("FABDEBUG> size=%lu\n", size); - - adapter_addresses = (char *)weaken(malloc)(size); - if (!adapter_addresses) { - return enomem(); - } - - rv = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, adapter_addresses, &size); - if (rv != kNtErrorSuccess) { - PRINTF("FABDEBUG> GetAdaptersAddresses failed %d\n", WSAGetLastError()); - weaken(free)(adapter_addresses); - return efault(); - } - - for (aa = (struct _IP_ADAPTER_ADDRESSES_XP *)adapter_addresses; aa != NULL; aa = aa->Next) { - PRINTF("FABDEBUG> Adapter name = %s", aa->AdapterName); - } - weaken(free)(adapter_addresses); - - return 0; -} -#else /* Reference: * - Description of ioctls: https://docs.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls * - Structure INTERFACE_INFO: https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-interface_info @@ -119,29 +45,250 @@ textwindows int ioctl_siocgifconf_nt(int fd, struct ifconf *ifc) { * - Using SIOCGIFCONF in Win32: https://docs.microsoft.com/en-us/windows/win32/winsock/using-unix-ioctls-in-winsock */ -textwindows int ioctl_siocgifconf_nt(int fd, struct ifconf *ifc) { - struct NtInterfaceInfo iflist[MAX_INTERFACES]; - uint32_t dwBytes; - int ret; - int i, count; - if (g_fds.p[fd].kind != kFdSocket) { - return ebadf(); - } +/* Design note: + * Because the Linux struct ifreq.ifr_name has a max length of 16, this could + * potentially lead to ambiguouity in interfaces. + * E.g. "Ethernet Interface 1", "Ethernet Interface 2" they are both truncated + * as "Ethernet Interfa" + * Since subsequent ioctl() (e.g. SIOCGIFFLAGS) uses this name to + * identify the speficif interface, we need to make sure that all the returned + * names are unique and have a way to corelate to the original adapter addresses + * record. + */ +struct HostAdapterDesc { + char name[IFNAMSIZ]; /* Given name */ + NtIpAdapterAddresses * addresses; /* NULL = invalid record */ +}; - ret = WSAIoctl(g_fds.p[fd].handle, kNtSioGetInterfaceList, NULL, 0, &iflist, sizeof(iflist), &dwBytes, NULL, NULL); - if (ret == -1) { - PRINTF("FABDEBUG> WSAIoctl failed %d\n", WSAGetLastError()); - return weaken(__winsockerr)(); - } +NtIpAdapterAddresses * theHostAdapterAddress; +struct HostAdapterDesc theAdapterName[MAX_INTERFACES]; - PRINTF("FABDEBUG> WSAIoctl success\n"); - count = dwBytes / sizeof(struct NtInterfaceInfo); - PRINTF("CI> SIO_GET_INTERFACE_LIST success:\n"); - for (i = 0; i < count; ++i) { - PRINTF("CI>\t #i: addr=%08x, flags=%08x, bcast=%08x\n", i, iflist[i].iiAddress.sin_addr.s_addr, iflist[i].iiFlags, iflist[i].iiBroadcastAddress.sin_addr.s_addr); - } - return ret; + +/* Given a short adapter name, look into theAdapterName to see if there is + * an adapter with the same name. Returns the index of theAdapterName + * if found, or -1 if not found + */ +static int findAdapterByName(const char *name) { + int i; + for (i = 0; (i < MAX_INTERFACES) && theAdapterName[i].addresses; ++i) { + if (!strncmp(name, theAdapterName[i].name, IFNAMSIZ)) { + /* Found */ + return i; + } + } + /* Not found */ + return -1; } -#endif +static void freeAdapterInfo(void) { + if (theHostAdapterAddress) { + /* Free any existing adapter informations */ + weaken(free)(theHostAdapterAddress); + theHostAdapterAddress = NULL; + memset(theAdapterName, 0, sizeof(theAdapterName)); + } +} + + +/* Sets theHostAdapterAddress. + * Returns -1 in case of failure (setting errno appropriately), 0 if success */ +static int _readAdapterAddresses(void) { + uint32_t size, rc; + PRINTF("FABDEBUG> _readAdapterAddresses:\n"); + + assert(theHostAdapterAddress == NULL); + assert(weaken(GetAdaptersAddresses)); + assert(weaken(WideCharToMultiByte)); + + /* Calculate the required data size + * Note: alternatively you can use AF_UNSPEC to also return IPv6 interfaces + */ + rc = weaken(GetAdaptersAddresses)(AF_INET, + NT_GAA_FLAG_INCLUDE_PREFIX, + NULL, /* Reserved */ + NULL, /* Ptr */ + &size); + if (rc != kNtErrorBufferOverflow) { + PRINTF("FABDEBUG> \tGetAdaptersAddresses failed with error %d\n", WSAGetLastError()); + return ebadf(); + } + PRINTF("FABDEBUG> \tsize required=%lu, allocating...\n", size); + // TODO: Do we need to access malloc/free through a weak ref? + theHostAdapterAddress = (NtIpAdapterAddresses *)weaken(malloc)(size); + if (!theHostAdapterAddress) { + PRINTF("FABDEBUG> \tmalloc failed\n"); + return enomem(); + } + + /* Re-run GetAdaptersAddresses this time with a valid buffer */ + rc = weaken(GetAdaptersAddresses)(AF_INET, + NT_GAA_FLAG_INCLUDE_PREFIX, + NULL, + theHostAdapterAddress, + &size); + if (rc != kNtErrorSuccess) { + PRINTF("FABDEBUG> GetAdaptersAddresses failed %d\n", WSAGetLastError()); + freeAdapterInfo(); + return efault(); + } + PRINTF("FABDEBUG> \tDone reading.\n"); + return 0; +} + + +void printUnicastAddressList(NtPIpAdapterUnicastAddress addr) { + const char * sep = ""; + for(; addr; addr=addr->Next) { + PRINTF("%s%s", sep, weaken(inet_ntoa)( + ((struct sockaddr_in *)(addr->Address.lpSockaddr))->sin_addr)); + if (!sep[0]) { + sep=", "; + } + } +} + +static int insertAdapterName(NtPIpAdapterAddresses aa) { + char name[IFNAMSIZ]; + int i; + int sfx; + + memset(name, 0, sizeof(name)); + /* On Windows, wchar_t is 16 bit, Linux is 32 bit, cannot use wcstombs() */ + weaken(WideCharToMultiByte)(0, 0, aa->FriendlyName, -1, name, IFNAMSIZ, NULL, NULL); + + /* Ensure the name is unique */ + for (sfx = 0; sfx < 10; ++sfx) { + i = findAdapterByName(name); + if (i != -1) { + /* Found a duplicate */ + name[strlen(name)-1] = '0' + sfx; + continue; + } + /* Unique */ + for (i = 0; i < MAX_INTERFACES; ++i) { + if (theAdapterName[i].addresses == NULL) { + break; + } + } + if (i == MAX_INTERFACES) { + /* Too many interfaces */ + break; + } + strncpy(theAdapterName[i].name, name, IFNAMSIZ-1); + theAdapterName[i].name[IFNAMSIZ-1] = '\0'; + theAdapterName[i].addresses = aa; + return i; + } + /* Reached max of 10 duplicates */ + return -1; +} + + + +textwindows int ioctl_siocgifconf_nt(int fd, struct ifconf *ifc) { + NtPIpAdapterAddresses aa; + struct ifreq *ptr; + int i; + + PRINTF("---------------------------------------\n"); + PRINTF("Sizeof() = %d\n", sizeof(NtIpAdapterAddresses)); + PRINTF("Next = %d\n", offsetof(NtIpAdapterAddresses, Next)); + PRINTF("AdapterName = %d\n", offsetof(NtIpAdapterAddresses, AdapterName)); + PRINTF("FirstUnicastAddress = %d\n", offsetof(NtIpAdapterAddresses, FirstUnicastAddress)); + PRINTF("FirstAnycastAddress = %d\n", offsetof(NtIpAdapterAddresses, FirstAnycastAddress)); + PRINTF("FirstMulticastAddress = %d\n", offsetof(NtIpAdapterAddresses, FirstMulticastAddress)); + PRINTF("FirstDnsServerAddress = %d\n", offsetof(NtIpAdapterAddresses, FirstDnsServerAddress)); + PRINTF("DnsSuffix = %d\n", offsetof(NtIpAdapterAddresses, DnsSuffix)); + PRINTF("Description = %d\n", offsetof(NtIpAdapterAddresses, Description)); + PRINTF("FriendlyName = %d\n", offsetof(NtIpAdapterAddresses, FriendlyName)); + + PRINTF("OperStatus = %d\n", offsetof(NtIpAdapterAddresses, OperStatus)); + PRINTF("Flags = %d\n", offsetof(NtIpAdapterAddresses, Flags)); + PRINTF("---------------------------------------\n"); + + if (theHostAdapterAddress) { + freeAdapterInfo(); + } + if (_readAdapterAddresses() == -1) { + return -1; + } + + for (ptr = ifc->ifc_req, aa = theHostAdapterAddress; + (aa != NULL) && ((char *)(ptr+1) - ifc->ifc_buf) < ifc->ifc_len; + aa = aa->Next, ptr++) { + + i = insertAdapterName(aa); + if (i == -1) { + PRINTF("FABDEBUG> Too many adapters\n"); + freeAdapterInfo(); + return enomem(); + } + strncpy(ptr->ifr_name, theAdapterName[i].name, IFNAMSIZ-1); + ptr->ifr_name[IFNAMSIZ-1]='\0'; + + PRINTF("FABDEBUG> Adapter name = %s\n", ptr->ifr_name); + if (aa->FirstUnicastAddress) { + ptr->ifr_addr = *aa->FirstUnicastAddress->Address.lpSockaddr; /* Windows sockaddr is compatible with Linux */ + PRINTF("FABDEBUG> \tIP="); + printUnicastAddressList(aa->FirstUnicastAddress); + PRINTF("\n"); + } + } + ifc->ifc_len = (char *)ptr - ifc->ifc_buf; + + return 0; + +} + +/* Performs the SIOCGIFADDR operation */ +int ioctl_siocgifaddr_nt(int fd, struct ifreq *ifr) { + NtPIpAdapterAddresses aa; + int i; + + i = findAdapterByName(ifr->ifr_name); + if (i == -1) { + PRINTF("FABDEBUG> Bad adapter\n"); + return ebadf(); + } + + aa = theAdapterName[i].addresses; + if (aa->FirstUnicastAddress) { + ifr->ifr_addr = *aa->FirstUnicastAddress->Address.lpSockaddr; /* Windows sockaddr is compatible with Linux */ + } + return 0; +} + +/* Performs the SIOCGIFFLAGS operation */ +int ioctl_siocgifflags_nt(int fd, struct ifreq *ifr) { + NtPIpAdapterAddresses aa; + int i; + + i = findAdapterByName(ifr->ifr_name); + if (i == -1) { + PRINTF("FABDEBUG> Bad adapter\n"); + return ebadf(); + } + + aa = theAdapterName[i].addresses; + ifr->ifr_flags = 0; + + /* Flags to consider: + * IFF_UP + * IFF_BROADCAST ** TODO: We need to validate + * IFF_LOOPBACK + * IFF_POINTOPOINT + * IFF_RUNNING ** Same as IFF_UP for now + * IFF_PROMISC ** NOT SUPPORTED, unknown how to retrieve it + */ + if (aa->OperStatus == IfOperStatusUp) ifr->ifr_flags |= IFF_UP | IFF_RUNNING; + if (aa->IfType == NT_IF_TYPE_PPP) ifr->ifr_flags |= IFF_POINTOPOINT; + //if (aa->TunnelType != TUNNEL_TYPE_NONE) ifr->ifr_flags |= IFF_POINTOPOINT; + if (aa->IfType == NT_IF_TYPE_SOFTWARE_LOOPBACK) ifr->ifr_flags |= IFF_LOOPBACK; + if (aa->FirstPrefix != NULL) ifr->ifr_flags |= IFF_BROADCAST; + + return 0; +} + + + diff --git a/libc/calls/ioctl-siocgifconf.c b/libc/calls/ioctl-siocgifconf.c index c8c5d5222..305015381 100644 --- a/libc/calls/ioctl-siocgifconf.c +++ b/libc/calls/ioctl-siocgifconf.c @@ -35,7 +35,8 @@ int ioctl_default(int, uint64_t, void *) hidden; int ioctl_siocgifconf_nt(int, struct ifconf *) hidden; -//int ioctl_siocgifaddr_nt(int, struct ifconf *) hidden; +int ioctl_siocgifaddr_nt(int, struct ifreq *) hidden; +int ioctl_siocgifflags_nt(int, struct ifreq *) hidden; static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) { if (IsBsd()) { @@ -131,8 +132,7 @@ int ioctl_siocgifaddr(int fd, void *ifr) { if (!IsWindows()) { return ioctl_siocgifaddr_sysv(fd, SIOCGIFADDR, (struct ifreq *)ifr); } else { - return enotsup(); - //return ioctl_siocgifaddr_nt(fd, ifc); + return ioctl_siocgifaddr_nt(fd, (struct ifreq *)ifr); } } @@ -168,7 +168,6 @@ int ioctl_siocgifflags(int fd, void *ifr) { /* Both BSD and Linux are for once compatible here... */ return ioctl_default(fd, SIOCGIFFLAGS, ifr); } else { - return enotsup(); - //return ioctl_siocgifflags_nt(fd, ifc); + return ioctl_siocgifflags_nt(fd, (struct ifreq *)ifr); } } diff --git a/libc/sock/socket-nt.c b/libc/sock/socket-nt.c index f49efe4c7..c79b50055 100644 --- a/libc/sock/socket-nt.c +++ b/libc/sock/socket-nt.c @@ -28,6 +28,7 @@ /* ioctl(SIOCGIFCONFIG) uses GetAdaptersAddresses as weak ref */ STATIC_YOINK("GetAdaptersAddresses"); +STATIC_YOINK("WideCharToMultiByte"); textwindows int sys_socket_nt(int family, int type, int protocol) { int64_t h;