mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-01 02:02:28 +00:00
Add IPv6 support to getifaddrs() on Linux (#1415)
Some checks failed
build / matrix_on_mode () (push) Has been cancelled
build / matrix_on_mode (optlinux) (push) Has been cancelled
build / matrix_on_mode (rel) (push) Has been cancelled
build / matrix_on_mode (tiny) (push) Has been cancelled
build / matrix_on_mode (tinylinux) (push) Has been cancelled
Some checks failed
build / matrix_on_mode () (push) Has been cancelled
build / matrix_on_mode (optlinux) (push) Has been cancelled
build / matrix_on_mode (rel) (push) Has been cancelled
build / matrix_on_mode (tiny) (push) Has been cancelled
build / matrix_on_mode (tinylinux) (push) Has been cancelled
This commit is contained in:
parent
2fe8338f92
commit
f1e83d5240
3 changed files with 256 additions and 33 deletions
|
@ -18,13 +18,19 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sock/ifaddrs.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/struct/ifconf.h"
|
||||
#include "libc/sock/struct/ifreq.h"
|
||||
#include "libc/sock/struct/sockaddr6.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/iff.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sio.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
|
||||
|
@ -36,6 +42,20 @@ struct IfAddr {
|
|||
struct sockaddr_in bstaddr;
|
||||
};
|
||||
|
||||
struct IfAddr6Info {
|
||||
int addr_scope;
|
||||
int addr_flags;
|
||||
};
|
||||
|
||||
struct IfAddr6 {
|
||||
struct ifaddrs ifaddrs;
|
||||
char name[IFNAMSIZ];
|
||||
struct sockaddr_in6 addr;
|
||||
struct sockaddr_in6 netmask;
|
||||
struct sockaddr_in6 bstaddr; // unused
|
||||
struct IfAddr6Info info;
|
||||
};
|
||||
|
||||
/**
|
||||
* Frees network interface address list.
|
||||
*/
|
||||
|
@ -48,6 +68,73 @@ void freeifaddrs(struct ifaddrs *ifp) {
|
|||
}
|
||||
}
|
||||
|
||||
// hex repr to network order int
|
||||
static uint128_t hex2no(const char *str) {
|
||||
uint128_t res = 0;
|
||||
const int max_quads = sizeof(uint128_t) * 2;
|
||||
int i = 0;
|
||||
while ((i < max_quads) && str[i]) {
|
||||
uint8_t acc = (((str[i] & 0xF) + (str[i] >> 6)) | ((str[i] >> 3) & 0x8));
|
||||
acc = acc << 4;
|
||||
i += 1;
|
||||
if (str[i]) {
|
||||
acc = acc | (((str[i] & 0xF) + (str[i] >> 6)) | ((str[i] >> 3) & 0x8));
|
||||
i += 1;
|
||||
}
|
||||
res = (res >> 8) | (((uint128_t)acc) << ((sizeof(uint128_t) - 1) * 8));
|
||||
}
|
||||
res = res >> ((max_quads - i) * 4);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets network interface IPv6 address list on linux.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
static int getifaddrs_linux_ip6(struct ifconf *conf) {
|
||||
int fd;
|
||||
int n = 0;
|
||||
struct ifreq *ifreq = conf->ifc_req;
|
||||
const int bufsz = 44 + IFNAMSIZ + 1;
|
||||
char buf[bufsz + 1]; // one line max size
|
||||
if ((fd = sys_openat(0, "/proc/net/if_inet6", O_RDONLY, 0)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((n = sys_read(fd, &buf[n], bufsz - n)) &&
|
||||
((char *)ifreq < (conf->ifc_buf + conf->ifc_len))) {
|
||||
// flags linux include/uapi/linux/if_addr.h:44
|
||||
// scope linux include/net/ipv6.h:L99
|
||||
|
||||
// *addr, *index, *plen, *scope, *flags, *ifname
|
||||
char *s[] = {&buf[0], &buf[33], &buf[36], &buf[39], &buf[42], &buf[45]};
|
||||
int ifnamelen = 0;
|
||||
while (*s[5] == ' ') {
|
||||
++s[5];
|
||||
}
|
||||
while (s[5][ifnamelen] > '\n') {
|
||||
++ifnamelen;
|
||||
}
|
||||
buf[32] = buf[35] = buf[38] = buf[41] = buf[44] = s[5][ifnamelen] = '\0';
|
||||
bzero(ifreq, sizeof(*ifreq));
|
||||
ifreq->ifr_addr.sa_family = AF_INET6;
|
||||
memcpy(&ifreq->ifr_name, s[5], ifnamelen);
|
||||
*((uint128_t *)&ifreq->ifr6_addr) = hex2no(s[0]);
|
||||
ifreq->ifr6_ifindex = hex2no(s[1]);
|
||||
ifreq->ifr6_prefixlen = hex2no(s[2]);
|
||||
ifreq->ifr6_scope = hex2no(s[3]);
|
||||
ifreq->ifr6_flags = hex2no(s[4]);
|
||||
++ifreq;
|
||||
int tlen = &s[5][ifnamelen] - &buf[0] + 1;
|
||||
n = bufsz - tlen;
|
||||
memcpy(&buf, &buf[tlen], n);
|
||||
}
|
||||
|
||||
conf->ifc_len = (char *)ifreq - conf->ifc_buf;
|
||||
return sys_close(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets network interface address list.
|
||||
*
|
||||
|
@ -55,6 +142,7 @@ void freeifaddrs(struct ifaddrs *ifp) {
|
|||
* @see tool/viz/getifaddrs.c for example code
|
||||
*/
|
||||
int getifaddrs(struct ifaddrs **out_ifpp) {
|
||||
// printf("%d\n", sizeof(struct ifreq));
|
||||
int rc = -1;
|
||||
int fd;
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) != -1) {
|
||||
|
@ -65,42 +153,88 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
|
|||
conf.ifc_buf = data;
|
||||
conf.ifc_len = size;
|
||||
if (!ioctl(fd, SIOCGIFCONF, &conf)) {
|
||||
if (IsLinux()) {
|
||||
struct ifconf confl6;
|
||||
confl6.ifc_buf = data + conf.ifc_len;
|
||||
confl6.ifc_len = size - conf.ifc_len;
|
||||
if ((rc = getifaddrs_linux_ip6(&confl6)))
|
||||
return rc;
|
||||
conf.ifc_len += confl6.ifc_len;
|
||||
}
|
||||
|
||||
struct ifaddrs *res = 0;
|
||||
for (struct ifreq *ifr = (struct ifreq *)data;
|
||||
(char *)ifr < data + conf.ifc_len; ++ifr) {
|
||||
if (ifr->ifr_addr.sa_family != AF_INET) {
|
||||
continue; // TODO(jart): IPv6 support
|
||||
}
|
||||
struct IfAddr *addr;
|
||||
if ((addr = calloc(1, sizeof(struct IfAddr)))) {
|
||||
memcpy(addr->name, ifr->ifr_name, IFNAMSIZ);
|
||||
addr->ifaddrs.ifa_name = addr->name;
|
||||
memcpy(&addr->addr, &ifr->ifr_addr, sizeof(struct sockaddr_in));
|
||||
addr->ifaddrs.ifa_addr = (struct sockaddr *)&addr->addr;
|
||||
addr->ifaddrs.ifa_netmask = (struct sockaddr *)&addr->netmask;
|
||||
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
|
||||
addr->ifaddrs.ifa_flags = ifr->ifr_flags;
|
||||
uint16_t family = ifr->ifr_addr.sa_family;
|
||||
if (family == AF_INET) {
|
||||
struct IfAddr *addr;
|
||||
if ((addr = calloc(1, sizeof(struct IfAddr)))) {
|
||||
memcpy(addr->name, ifr->ifr_name, IFNAMSIZ);
|
||||
addr->ifaddrs.ifa_name = addr->name;
|
||||
memcpy(&addr->addr, &ifr->ifr_addr, sizeof(struct sockaddr_in));
|
||||
addr->ifaddrs.ifa_addr = (struct sockaddr *)&addr->addr;
|
||||
addr->ifaddrs.ifa_netmask = (struct sockaddr *)&addr->netmask;
|
||||
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
|
||||
addr->ifaddrs.ifa_flags = ifr->ifr_flags;
|
||||
}
|
||||
if (!ioctl(fd, SIOCGIFNETMASK, ifr)) {
|
||||
memcpy(&addr->netmask, &ifr->ifr_addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
}
|
||||
unsigned long op;
|
||||
if (addr->ifaddrs.ifa_flags & IFF_BROADCAST) {
|
||||
op = SIOCGIFBRDADDR;
|
||||
} else if (addr->ifaddrs.ifa_flags & IFF_POINTOPOINT) {
|
||||
op = SIOCGIFDSTADDR;
|
||||
} else {
|
||||
op = 0;
|
||||
}
|
||||
if (op && !ioctl(fd, op, ifr)) {
|
||||
memcpy(&addr->bstaddr, &ifr->ifr_addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
addr->ifaddrs.ifa_broadaddr = // is union'd w/ ifu_dstaddr
|
||||
(struct sockaddr *)&addr->bstaddr;
|
||||
}
|
||||
addr->ifaddrs.ifa_next = res;
|
||||
res = (struct ifaddrs *)addr;
|
||||
}
|
||||
if (!ioctl(fd, SIOCGIFNETMASK, ifr)) {
|
||||
memcpy(&addr->netmask, &ifr->ifr_addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
} else if (family == AF_INET6) {
|
||||
struct IfAddr6 *addr6;
|
||||
if ((addr6 = calloc(1, sizeof(struct IfAddr6)))) {
|
||||
addr6->ifaddrs.ifa_name = addr6->name;
|
||||
addr6->ifaddrs.ifa_addr = (struct sockaddr *)&addr6->addr;
|
||||
addr6->ifaddrs.ifa_netmask = (struct sockaddr *)&addr6->netmask;
|
||||
addr6->ifaddrs.ifa_broadaddr = (struct sockaddr *)&addr6->bstaddr;
|
||||
addr6->ifaddrs.ifa_data = (void *)&addr6->info;
|
||||
|
||||
memcpy(&addr6->name, &ifr->ifr_name, IFNAMSIZ);
|
||||
addr6->info.addr_flags = ifr->ifr6_flags;
|
||||
addr6->info.addr_scope = ifr->ifr6_scope;
|
||||
|
||||
addr6->addr.sin6_family = AF_INET6;
|
||||
addr6->addr.sin6_port = 0;
|
||||
addr6->addr.sin6_flowinfo = 0;
|
||||
addr6->addr.sin6_scope_id = ifr->ifr6_ifindex;
|
||||
memcpy(&addr6->addr.sin6_addr, &ifr->ifr6_addr,
|
||||
sizeof(struct in6_addr));
|
||||
|
||||
addr6->netmask.sin6_family = AF_INET6;
|
||||
addr6->netmask.sin6_port = 0;
|
||||
addr6->netmask.sin6_flowinfo = 0;
|
||||
addr6->addr.sin6_scope_id = ifr->ifr6_ifindex;
|
||||
memcpy(&addr6->netmask.sin6_addr, &ifr->ifr6_addr,
|
||||
sizeof(struct in6_addr));
|
||||
*((uint128_t *)&(addr6->netmask.sin6_addr)) &=
|
||||
(UINT128_MAX >> ifr->ifr6_prefixlen);
|
||||
|
||||
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
|
||||
addr6->ifaddrs.ifa_flags = ifr->ifr_flags;
|
||||
}
|
||||
|
||||
bzero(&addr6->bstaddr, sizeof(struct sockaddr_in6));
|
||||
addr6->ifaddrs.ifa_next = res;
|
||||
res = (struct ifaddrs *)addr6;
|
||||
}
|
||||
unsigned long op;
|
||||
if (addr->ifaddrs.ifa_flags & IFF_BROADCAST) {
|
||||
op = SIOCGIFBRDADDR;
|
||||
} else if (addr->ifaddrs.ifa_flags & IFF_POINTOPOINT) {
|
||||
op = SIOCGIFDSTADDR;
|
||||
} else {
|
||||
op = 0;
|
||||
}
|
||||
if (op && !ioctl(fd, op, ifr)) {
|
||||
memcpy(&addr->bstaddr, &ifr->ifr_addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
addr->ifaddrs.ifa_broadaddr = // is union'd w/ ifu_dstaddr
|
||||
(struct sockaddr *)&addr->bstaddr;
|
||||
}
|
||||
addr->ifaddrs.ifa_next = res;
|
||||
res = (struct ifaddrs *)addr;
|
||||
}
|
||||
}
|
||||
*out_ifpp = res;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_SOCK_STRUCT_IFREQ_H_
|
||||
#define COSMOPOLITAN_LIBC_SOCK_STRUCT_IFREQ_H_
|
||||
#include "libc/sock/struct/sockaddr.h"
|
||||
#include "libc/sock/struct/sockaddr6.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define IF_NAMESIZE 16
|
||||
|
@ -11,6 +12,14 @@ struct ifreq {
|
|||
char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */
|
||||
} ifr_ifrn;
|
||||
union {
|
||||
struct {
|
||||
uint16_t sa_family;
|
||||
uint16_t ifr6_ifindex; /* Interface index */
|
||||
uint16_t ifr6_flags; /* Flags */
|
||||
uint8_t ifr6_scope; /* Addr scope */
|
||||
uint8_t ifr6_prefixlen; /* Prefix length */
|
||||
struct in6_addr ifr6_addr;
|
||||
} in6;
|
||||
struct sockaddr ifru_addr; /* SIOCGIFADDR */
|
||||
struct sockaddr ifru_dstaddr; /* SIOCGIFDSTADDR */
|
||||
struct sockaddr ifru_netmask; /* SIOCGIFNETMASK */
|
||||
|
@ -29,5 +38,11 @@ struct ifreq {
|
|||
#define ifr_flags ifr_ifru.ifru_flags /* flags */
|
||||
#define ifr_ifindex ifr_ifru.ifru_ivalue
|
||||
|
||||
#define ifr6_addr ifr_ifru.in6.ifr6_addr /* IP6 Addr */
|
||||
#define ifr6_scope ifr_ifru.in6.ifr6_scope /* IP6 Addr scope */
|
||||
#define ifr6_prefixlen ifr_ifru.in6.ifr6_prefixlen /* IP6 Prefix length */
|
||||
#define ifr6_ifindex ifr_ifru.in6.ifr6_ifindex /* IP6 If index */
|
||||
#define ifr6_flags ifr_ifru.in6.ifr6_flags /* IP6 If flags */
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_SOCK_STRUCT_IFREQ_H_ */
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
eth0
|
||||
addr: 10.10.10.237
|
||||
netmask: 255.255.255.0
|
||||
broadcast: 255.255.255.0
|
||||
broadcast: 10.10.10.255
|
||||
flags: IFF_UP IFF_BROADCAST IFF_MULTICAST IFF_RUNNING
|
||||
|
||||
lo
|
||||
|
@ -74,13 +74,87 @@ int main(int argc, char *argv[]) {
|
|||
tinyprint(1, "netmask: ", buf, "\n", NULL);
|
||||
}
|
||||
if ((ifa->ifa_flags & IFF_BROADCAST) &&
|
||||
sockaddr2str(ifa->ifa_netmask, buf, sizeof(buf))) {
|
||||
sockaddr2str(ifa->ifa_broadaddr, buf, sizeof(buf))) {
|
||||
tinyprint(1, "broadcast: ", buf, "\n", NULL);
|
||||
} else if ((ifa->ifa_flags & IFF_POINTOPOINT) &&
|
||||
sockaddr2str(ifa->ifa_dstaddr, buf, sizeof(buf))) {
|
||||
tinyprint(1, "dstaddr: ", buf, "\n", NULL);
|
||||
}
|
||||
|
||||
if (ifa->ifa_addr->sa_family == AF_INET6) {
|
||||
int scope = ((int *)ifa->ifa_data)[0];
|
||||
int aflags = ((int *)ifa->ifa_data)[1];
|
||||
// #define IPV6_ADDR_LOOPBACK 0x0010U
|
||||
// #define IPV6_ADDR_LINKLOCAL 0x0020U
|
||||
// #define IPV6_ADDR_SITELOCAL 0x0040U
|
||||
|
||||
// #define IFA_F_TEMPORARY 0x01
|
||||
// #define IFA_F_NODAD 0x02
|
||||
// #define IFA_F_OPTIMISTIC 0x04
|
||||
// #define IFA_F_DADFAILED 0x08
|
||||
// #define IFA_F_HOMEADDRESS 0x10
|
||||
// #define IFA_F_DEPRECATED 0x20
|
||||
// #define IFA_F_TENTATIVE 0x40
|
||||
// #define IFA_F_PERMANENT 0x80
|
||||
// #define IFA_F_MANAGETEMPADDR 0x100
|
||||
// #define IFA_F_NOPREFIXROUTE 0x200
|
||||
// #define IFA_F_MCAUTOJOIN 0x400
|
||||
// #define IFA_F_STABLE_PRIVACY 0x800
|
||||
tinyprint(1, "scope:", NULL);
|
||||
if (scope == 0x10) {
|
||||
tinyprint(1, " loopback", NULL);
|
||||
}
|
||||
if (scope == 0x20) {
|
||||
tinyprint(1, " linklocal", NULL);
|
||||
}
|
||||
if (scope == 0x40) {
|
||||
tinyprint(1, " sitelocal", NULL);
|
||||
}
|
||||
if (scope == 0x00) {
|
||||
tinyprint(1, " global", NULL);
|
||||
}
|
||||
tinyprint(1, "\n", NULL);
|
||||
|
||||
tinyprint(1, "addr flags:", NULL);
|
||||
if (aflags & 0x01) {
|
||||
tinyprint(1, " temporary", NULL);
|
||||
}
|
||||
if (aflags & 0x02) {
|
||||
tinyprint(1, " nodad", NULL);
|
||||
}
|
||||
if (aflags & 0x04) {
|
||||
tinyprint(1, " optimistic", NULL);
|
||||
}
|
||||
if (aflags & 0x08) {
|
||||
tinyprint(1, " dadfailed", NULL);
|
||||
}
|
||||
if (aflags & 0x10) {
|
||||
tinyprint(1, " homeaddress", NULL);
|
||||
}
|
||||
if (aflags & 0x20) {
|
||||
tinyprint(1, " deprecated", NULL);
|
||||
}
|
||||
if (aflags & 0x40) {
|
||||
tinyprint(1, " tentative", NULL);
|
||||
}
|
||||
if (aflags & 0x80) {
|
||||
tinyprint(1, " permanent", NULL);
|
||||
}
|
||||
if (aflags & 0x100) {
|
||||
tinyprint(1, " managetempaddr", NULL);
|
||||
}
|
||||
if (aflags & 0x200) {
|
||||
tinyprint(1, " noprefixroute", NULL);
|
||||
}
|
||||
if (aflags & 0x400) {
|
||||
tinyprint(1, " mcautojoin", NULL);
|
||||
}
|
||||
if (aflags & 0x800) {
|
||||
tinyprint(1, " stable_privacy", NULL);
|
||||
}
|
||||
tinyprint(1, "\n", NULL);
|
||||
}
|
||||
|
||||
tinyprint(1, "flags:", NULL);
|
||||
if (ifa->ifa_flags & IFF_UP) {
|
||||
tinyprint(1, " IFF_UP", NULL);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue