diff --git a/libc/calls/ioctl-siocgifconf.c b/libc/calls/ioctl-siocgifconf.c index e967c3f9f..a862e6029 100644 --- a/libc/calls/ioctl-siocgifconf.c +++ b/libc/calls/ioctl-siocgifconf.c @@ -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); + } +} diff --git a/libc/calls/ioctl.h b/libc/calls/ioctl.h index 6b6eb19cf..b673f852f 100644 --- a/libc/calls/ioctl.h +++ b/libc/calls/ioctl.h @@ -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) { diff --git a/libc/sock/internal.h b/libc/sock/internal.h index da19fb706..5ebf63235 100644 --- a/libc/sock/internal.h +++ b/libc/sock/internal.h @@ -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; diff --git a/libc/sock/sock.h b/libc/sock/sock.h index 65c0e8018..dd2ee4964 100644 --- a/libc/sock/sock.h +++ b/libc/sock/sock.h @@ -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)