Fixed all the issues related to BSD for the implementation of SIGCGIFCONF, SIGCGIFADDR, and SIOCGIFNETMASK ioctls

This commit is contained in:
Fabrizio Bertocci 2021-05-21 18:37:17 -04:00
parent f6388b2bf6
commit 1b8776dea7
4 changed files with 144 additions and 76 deletions

View file

@ -23,10 +23,6 @@
#include "libc/sock/internal.h"
#include "libc/sysv/consts/sio.h"
#include "libc/stdio/stdio.h"
#define PRINTF weaken(printf)
/* SIOCGIFCONF:
* Takes an struct ifconf object of a given size
* Modifies the following:
@ -39,66 +35,122 @@
int ioctl_siocgifconf_nt(int, struct ifconf *) hidden;
static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) {
/* Same as the default for now... */
if (IsBsd()) {
/* BSD uses a slightly different memory structure where:
* - sizeof(struct ifreq) is 32 bytes instead of 40 bytes
* - ifc.ifc_len is uint32_t instead of uint64_t
* - struct ifconf is #pragma pack(4) (instead of the default pack(8))
*/
int i;
char *buf; /* Temporary buffer to store ifreq */
char *pBsdReq; /* Scan through buf records */
char *pBsdEnd; /* End of the ifreq */
struct ifconf_bsd ifc_bsd;
struct ifreq_bsd *bsdReq;
struct ifreq *req;
size_t numReq = ifc->ifc_len / sizeof(struct ifreq);
if (!weaken(malloc)) {
return enomem();
} else {
/* On BSD the size of the struct ifreq is smaller (16 bytes
* instead of 24 bytes), so buffers need to be adjusted accordingly
*
* TODO: Since BSD requires a SMALLER buffer we don't need to
* malloc a temp buffer, insted reuse the same buffer and
* safely move overlapping ifrn_name chunks
*/
int i;
struct ifconf ifc_bsd;
size_t num_ifreq = ifc->ifc_len / sizeof(struct ifreq);
PRINTF("Mac version!\n");
ifc_bsd.ifc_len = (num_ifreq * sizeof(struct ifreq_bsd)); /* Adjust max buffer */
ifc_bsd.ifc_buf = weaken(malloc)(ifc_bsd.ifc_len);
PRINTF("numInterf Linux=%lu\n", num_ifreq);
PRINTF("BSD size=%lu\n", ifc_bsd.ifc_len);
PRINTF("Linux size=%lu\n", ifc->ifc_len);
if (!ifc_bsd.ifc_buf) {
PRINTF("Malloc failed\n");
return enomem();
}
PRINTF("Calling ioctl()\n");
i = sys_ioctl(fd, SIOCGIFCONF, &ifc_bsd);
PRINTF("rc=%d\n", i);
if (i < 0) {
weaken(free)(ifc_bsd.ifc_buf);
return -1;
}
/* Number of interfaces returned */
num_ifreq = ifc_bsd.ifc_len / sizeof(struct ifreq_bsd);
for (i = 1; i < num_ifreq; ++i) {
/* The first interface always match the same position */
memcpy(ifc->ifc_req[i].ifr_name, ifc_bsd.ifc_req[i].ifr_name, IFNAMSIZ);
}
ifc->ifc_len = num_ifreq * sizeof(struct ifreq);
weaken(free)(ifc_bsd.ifc_buf);
return 0;
}
ifc_bsd.ifc_len = numReq * sizeof(struct ifreq_bsd);
buf = weaken(malloc)(ifc_bsd.ifc_len);
if (!buf) {
return enomem();
}
ifc_bsd.ifc_buf = buf;
if ((i = sys_ioctl(fd, SIOCGIFCONF, &ifc_bsd)) < 0) {
weaken(free)(buf);
return -1;
}
/* On BSD the size of the struct ifreq is different than Linux.
* On Linux is fixed (40 bytes), but on BSD the struct sockaddr
* has variable length, making the whole struct ifreq a variable
* sized record.
*/
for (pBsdReq = buf, pBsdEnd = buf + ifc_bsd.ifc_len, req = ifc->ifc_req;
pBsdReq < pBsdEnd;
++req) {
bsdReq = (struct ifreq_bsd *)pBsdReq;
memcpy(req->ifr_name, bsdReq->ifr_name, IFNAMSIZ);
memcpy(&req->ifr_addr, &bsdReq->ifr_addr, sizeof(struct sockaddr_bsd));
sockaddr2linux(&req->ifr_addr);
pBsdReq += IFNAMSIZ + bsdReq->ifr_addr.sa_len;
}
/* Adjust len */
ifc->ifc_len = (size_t)((char *)req - ifc->ifc_buf);
weaken(free)(buf);
return 0;
} else {
// 100% compatible with Linux
/* 100% compatible with Linux */
return sys_ioctl(fd, SIOCGIFCONF, ifc);
}
}
static int ioctl_siocgifaddr_sysv(int fd, struct ifreq *ifr) {
int i;
if (IsBsd()) {
sockaddr2bsd(&ifr->ifr_addr);
}
if ((i = sys_ioctl(fd, SIOCGIFADDR, ifr)) < 0) {
return -1;
}
if (IsBsd()) {
sockaddr2linux(&ifr->ifr_addr);
}
return 0;
}
static int ioctl_siocgifnetmask_sysv(int fd, struct ifreq *ifr) {
int i;
if (IsBsd()) {
sockaddr2bsd(&ifr->ifr_netmask);
}
if ((i = sys_ioctl(fd, SIOCGIFNETMASK, ifr)) < 0) {
return -1;
}
if (IsBsd()) {
sockaddr2linux(&ifr->ifr_netmask);
}
return 0;
}
/**
* Returns information about network interfaces.
*
* @see ioctl(fd, SIOCGIFCONF, tio) dispatches here
*/
int ioctl_siocgifconf(int fd, struct ifconf *ifc) {
int ioctl_siocgifconf(int fd, void *ifc) {
if (!IsWindows()) {
return ioctl_siocgifconf_sysv(fd, ifc);
return ioctl_siocgifconf_sysv(fd, (struct ifconf *)ifc);
} else {
return ioctl_siocgifconf_nt(fd, ifc);
return enotsup();
//return ioctl_siocgifconf_nt(fd, ifc);
}
}
int ioctl_siocgifaddr(int fd, void *ifr) {
if (!IsWindows()) {
return ioctl_siocgifaddr_sysv(fd, (struct ifreq *)ifr);
} else {
return enotsup();
//return ioctl_siocgifaddr_nt(fd, ifc);
}
}
int ioctl_siocgifnetmask(int fd, void *ifr) {
if (!IsWindows()) {
return ioctl_siocgifnetmask_sysv(fd, (struct ifreq *)ifr);
} else {
return enotsup();
//return ioctl_siocgifaddr_nt(fd, ifc);
}
}

View file

@ -29,6 +29,8 @@ int ioctl(int, uint64_t, void *);
if (CMP(request, TCSETSW)) return ioctl_tcsets(FD, REQUEST, MEMORY); \
if (CMP(request, TCSETSF)) return ioctl_tcsets(FD, REQUEST, MEMORY); \
if (CMP(request, SIOCGIFCONF)) return ioctl_siocgifconf(FD, MEMORY); \
if (CMP(request, SIOCGIFADDR)) return ioctl_siocgifaddr(FD, MEMORY); \
if (CMP(request, SIOCGIFNETMASK)) return ioctl_siocgifnetmask(FD, MEMORY); \
} while (0)
/*
@ -44,6 +46,8 @@ int ioctl_tiocgwinsz_nt(int, void *);
int ioctl_tiocswinsz(int, void *);
int ioctl_tiocswinsz_nt(int, void *);
int ioctl_siocgifconf(int, void *);
int ioctl_siocgifaddr(int, void *);
int ioctl_siocgifnetmask(int, void *);
int ioctl_default(int, uint64_t, void *);
forceinline int ioctl_dispatch(int fd, uint64_t request, void *memory) {

View file

@ -53,6 +53,43 @@ struct sockaddr_un_bsd {
char sun_path[108];
};
/* -----------------------------------------------------------------------------------
* ioctl SIOCGIFCONF & others:
*
* BSD has a different structure. All the ioctl will adjust to use
* the ifreq with the ifreq_bsd
*/
struct ifreq_bsd {
union {
char ifrn_name[IFNAMSIZ];
} ifr_ifrn;
union {
/* Right now we only list the structures used by the few ioctl that are
* supported by Cosmopolitan. Add your own definition here if you need
* a particular ioctl that requires polyfill for BSD
*/
struct sockaddr_bsd ifru_addr;
struct sockaddr_bsd ifru_netmask;
short ifru_flags;
char ifru_pad[16]; /* used as padding */
} ifr_ifru;
};
#pragma pack(4)
struct ifconf_bsd {
uint32_t ifc_len; /* size of buffer */
union {
char *ifcu_buf;
struct ifreq_bsd *ifcu_req;
} ifc_ifcu;
};
#pragma pack()
/* ------------------------------------------------------------------------------------*/
struct SockFd {
int family;
int type;

View file

@ -87,7 +87,7 @@ struct msghdr { /* Linux+NT ABI */
* must know all networks accessible).
*/
struct ifconf {
int ifc_len; /* size of buffer */
uint64_t ifc_len; /* size of buffer */
union {
char *ifcu_buf;
struct ifreq *ifcu_req;
@ -109,41 +109,16 @@ struct ifreq {
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short ifru_flags;
int ifru_ivalue;
int ifru_mtu;
char ifru_pad[24]; /* ifru_map, used as padding */
} ifr_ifru;
};
struct ifreq_bsd {
union {
char ifrn_name[IFNAMSIZ];
} ifr_ifrn;
union {
char ifru_pad[16];
char ifru_pad[24]; /* ifru_map is the largest, just pad */
} ifr_ifru;
};
#define ifr_name ifr_ifrn.ifrn_name /* interface name */
#define ifr_addr ifr_ifru.ifru_addr /* address */
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
#define ifr_netmask ifr_ifru.ifru_netmask /* netmask */
#define ifr_flags ifr_ifru.ifru_flags /* flags */
#define ifr_metric ifr_ifru.ifru_ivalue /* metric */
#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
#define ifr_qlen ifr_ifru.ifru_ivalue /* queue length */
#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
/* ifru_map is unsupported */
#define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0)
#define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0)