Added initial implementation of SIOCGIFCONFIG, SIOCGIFADDR, SIOCGIFFLAGS

This commit is contained in:
Fabrizio Bertocci 2021-06-21 23:31:20 +02:00
parent 70828ee0ca
commit 08765c0850
3 changed files with 253 additions and 106 deletions

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;