Skip to content

Commit f1e83d5

Browse files
authored
Add IPv6 support to getifaddrs() on Linux (#1415)
1 parent 2fe8338 commit f1e83d5

File tree

3 files changed

+256
-33
lines changed

3 files changed

+256
-33
lines changed

libc/sock/ifaddrs.c

Lines changed: 165 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/sock/ifaddrs.h"
2020
#include "libc/calls/calls.h"
21+
#include "libc/calls/syscall-sysv.internal.h"
22+
#include "libc/dce.h"
23+
#include "libc/limits.h"
2124
#include "libc/mem/mem.h"
2225
#include "libc/sock/sock.h"
2326
#include "libc/sock/struct/ifconf.h"
2427
#include "libc/sock/struct/ifreq.h"
28+
#include "libc/sock/struct/sockaddr6.h"
29+
#include "libc/stdio/stdio.h"
2530
#include "libc/str/str.h"
2631
#include "libc/sysv/consts/af.h"
2732
#include "libc/sysv/consts/iff.h"
33+
#include "libc/sysv/consts/o.h"
2834
#include "libc/sysv/consts/sio.h"
2935
#include "libc/sysv/consts/sock.h"
3036

@@ -36,6 +42,20 @@ struct IfAddr {
3642
struct sockaddr_in bstaddr;
3743
};
3844

45+
struct IfAddr6Info {
46+
int addr_scope;
47+
int addr_flags;
48+
};
49+
50+
struct IfAddr6 {
51+
struct ifaddrs ifaddrs;
52+
char name[IFNAMSIZ];
53+
struct sockaddr_in6 addr;
54+
struct sockaddr_in6 netmask;
55+
struct sockaddr_in6 bstaddr; // unused
56+
struct IfAddr6Info info;
57+
};
58+
3959
/**
4060
* Frees network interface address list.
4161
*/
@@ -48,13 +68,81 @@ void freeifaddrs(struct ifaddrs *ifp) {
4868
}
4969
}
5070

71+
// hex repr to network order int
72+
static uint128_t hex2no(const char *str) {
73+
uint128_t res = 0;
74+
const int max_quads = sizeof(uint128_t) * 2;
75+
int i = 0;
76+
while ((i < max_quads) && str[i]) {
77+
uint8_t acc = (((str[i] & 0xF) + (str[i] >> 6)) | ((str[i] >> 3) & 0x8));
78+
acc = acc << 4;
79+
i += 1;
80+
if (str[i]) {
81+
acc = acc | (((str[i] & 0xF) + (str[i] >> 6)) | ((str[i] >> 3) & 0x8));
82+
i += 1;
83+
}
84+
res = (res >> 8) | (((uint128_t)acc) << ((sizeof(uint128_t) - 1) * 8));
85+
}
86+
res = res >> ((max_quads - i) * 4);
87+
return res;
88+
}
89+
90+
/**
91+
* Gets network interface IPv6 address list on linux.
92+
*
93+
* @return 0 on success, or -1 w/ errno
94+
*/
95+
static int getifaddrs_linux_ip6(struct ifconf *conf) {
96+
int fd;
97+
int n = 0;
98+
struct ifreq *ifreq = conf->ifc_req;
99+
const int bufsz = 44 + IFNAMSIZ + 1;
100+
char buf[bufsz + 1]; // one line max size
101+
if ((fd = sys_openat(0, "/proc/net/if_inet6", O_RDONLY, 0)) == -1) {
102+
return -1;
103+
}
104+
105+
while ((n = sys_read(fd, &buf[n], bufsz - n)) &&
106+
((char *)ifreq < (conf->ifc_buf + conf->ifc_len))) {
107+
// flags linux include/uapi/linux/if_addr.h:44
108+
// scope linux include/net/ipv6.h:L99
109+
110+
// *addr, *index, *plen, *scope, *flags, *ifname
111+
char *s[] = {&buf[0], &buf[33], &buf[36], &buf[39], &buf[42], &buf[45]};
112+
int ifnamelen = 0;
113+
while (*s[5] == ' ') {
114+
++s[5];
115+
}
116+
while (s[5][ifnamelen] > '\n') {
117+
++ifnamelen;
118+
}
119+
buf[32] = buf[35] = buf[38] = buf[41] = buf[44] = s[5][ifnamelen] = '\0';
120+
bzero(ifreq, sizeof(*ifreq));
121+
ifreq->ifr_addr.sa_family = AF_INET6;
122+
memcpy(&ifreq->ifr_name, s[5], ifnamelen);
123+
*((uint128_t *)&ifreq->ifr6_addr) = hex2no(s[0]);
124+
ifreq->ifr6_ifindex = hex2no(s[1]);
125+
ifreq->ifr6_prefixlen = hex2no(s[2]);
126+
ifreq->ifr6_scope = hex2no(s[3]);
127+
ifreq->ifr6_flags = hex2no(s[4]);
128+
++ifreq;
129+
int tlen = &s[5][ifnamelen] - &buf[0] + 1;
130+
n = bufsz - tlen;
131+
memcpy(&buf, &buf[tlen], n);
132+
}
133+
134+
conf->ifc_len = (char *)ifreq - conf->ifc_buf;
135+
return sys_close(fd);
136+
}
137+
51138
/**
52139
* Gets network interface address list.
53140
*
54141
* @return 0 on success, or -1 w/ errno
55142
* @see tool/viz/getifaddrs.c for example code
56143
*/
57144
int getifaddrs(struct ifaddrs **out_ifpp) {
145+
// printf("%d\n", sizeof(struct ifreq));
58146
int rc = -1;
59147
int fd;
60148
if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) != -1) {
@@ -65,42 +153,88 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
65153
conf.ifc_buf = data;
66154
conf.ifc_len = size;
67155
if (!ioctl(fd, SIOCGIFCONF, &conf)) {
156+
if (IsLinux()) {
157+
struct ifconf confl6;
158+
confl6.ifc_buf = data + conf.ifc_len;
159+
confl6.ifc_len = size - conf.ifc_len;
160+
if ((rc = getifaddrs_linux_ip6(&confl6)))
161+
return rc;
162+
conf.ifc_len += confl6.ifc_len;
163+
}
164+
68165
struct ifaddrs *res = 0;
69166
for (struct ifreq *ifr = (struct ifreq *)data;
70167
(char *)ifr < data + conf.ifc_len; ++ifr) {
71-
if (ifr->ifr_addr.sa_family != AF_INET) {
72-
continue; // TODO(jart): IPv6 support
73-
}
74-
struct IfAddr *addr;
75-
if ((addr = calloc(1, sizeof(struct IfAddr)))) {
76-
memcpy(addr->name, ifr->ifr_name, IFNAMSIZ);
77-
addr->ifaddrs.ifa_name = addr->name;
78-
memcpy(&addr->addr, &ifr->ifr_addr, sizeof(struct sockaddr_in));
79-
addr->ifaddrs.ifa_addr = (struct sockaddr *)&addr->addr;
80-
addr->ifaddrs.ifa_netmask = (struct sockaddr *)&addr->netmask;
81-
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
82-
addr->ifaddrs.ifa_flags = ifr->ifr_flags;
83-
}
84-
if (!ioctl(fd, SIOCGIFNETMASK, ifr)) {
85-
memcpy(&addr->netmask, &ifr->ifr_addr,
86-
sizeof(struct sockaddr_in));
168+
uint16_t family = ifr->ifr_addr.sa_family;
169+
if (family == AF_INET) {
170+
struct IfAddr *addr;
171+
if ((addr = calloc(1, sizeof(struct IfAddr)))) {
172+
memcpy(addr->name, ifr->ifr_name, IFNAMSIZ);
173+
addr->ifaddrs.ifa_name = addr->name;
174+
memcpy(&addr->addr, &ifr->ifr_addr, sizeof(struct sockaddr_in));
175+
addr->ifaddrs.ifa_addr = (struct sockaddr *)&addr->addr;
176+
addr->ifaddrs.ifa_netmask = (struct sockaddr *)&addr->netmask;
177+
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
178+
addr->ifaddrs.ifa_flags = ifr->ifr_flags;
179+
}
180+
if (!ioctl(fd, SIOCGIFNETMASK, ifr)) {
181+
memcpy(&addr->netmask, &ifr->ifr_addr,
182+
sizeof(struct sockaddr_in));
183+
}
184+
unsigned long op;
185+
if (addr->ifaddrs.ifa_flags & IFF_BROADCAST) {
186+
op = SIOCGIFBRDADDR;
187+
} else if (addr->ifaddrs.ifa_flags & IFF_POINTOPOINT) {
188+
op = SIOCGIFDSTADDR;
189+
} else {
190+
op = 0;
191+
}
192+
if (op && !ioctl(fd, op, ifr)) {
193+
memcpy(&addr->bstaddr, &ifr->ifr_addr,
194+
sizeof(struct sockaddr_in));
195+
addr->ifaddrs.ifa_broadaddr = // is union'd w/ ifu_dstaddr
196+
(struct sockaddr *)&addr->bstaddr;
197+
}
198+
addr->ifaddrs.ifa_next = res;
199+
res = (struct ifaddrs *)addr;
87200
}
88-
unsigned long op;
89-
if (addr->ifaddrs.ifa_flags & IFF_BROADCAST) {
90-
op = SIOCGIFBRDADDR;
91-
} else if (addr->ifaddrs.ifa_flags & IFF_POINTOPOINT) {
92-
op = SIOCGIFDSTADDR;
93-
} else {
94-
op = 0;
95-
}
96-
if (op && !ioctl(fd, op, ifr)) {
97-
memcpy(&addr->bstaddr, &ifr->ifr_addr,
98-
sizeof(struct sockaddr_in));
99-
addr->ifaddrs.ifa_broadaddr = // is union'd w/ ifu_dstaddr
100-
(struct sockaddr *)&addr->bstaddr;
201+
} else if (family == AF_INET6) {
202+
struct IfAddr6 *addr6;
203+
if ((addr6 = calloc(1, sizeof(struct IfAddr6)))) {
204+
addr6->ifaddrs.ifa_name = addr6->name;
205+
addr6->ifaddrs.ifa_addr = (struct sockaddr *)&addr6->addr;
206+
addr6->ifaddrs.ifa_netmask = (struct sockaddr *)&addr6->netmask;
207+
addr6->ifaddrs.ifa_broadaddr = (struct sockaddr *)&addr6->bstaddr;
208+
addr6->ifaddrs.ifa_data = (void *)&addr6->info;
209+
210+
memcpy(&addr6->name, &ifr->ifr_name, IFNAMSIZ);
211+
addr6->info.addr_flags = ifr->ifr6_flags;
212+
addr6->info.addr_scope = ifr->ifr6_scope;
213+
214+
addr6->addr.sin6_family = AF_INET6;
215+
addr6->addr.sin6_port = 0;
216+
addr6->addr.sin6_flowinfo = 0;
217+
addr6->addr.sin6_scope_id = ifr->ifr6_ifindex;
218+
memcpy(&addr6->addr.sin6_addr, &ifr->ifr6_addr,
219+
sizeof(struct in6_addr));
220+
221+
addr6->netmask.sin6_family = AF_INET6;
222+
addr6->netmask.sin6_port = 0;
223+
addr6->netmask.sin6_flowinfo = 0;
224+
addr6->addr.sin6_scope_id = ifr->ifr6_ifindex;
225+
memcpy(&addr6->netmask.sin6_addr, &ifr->ifr6_addr,
226+
sizeof(struct in6_addr));
227+
*((uint128_t *)&(addr6->netmask.sin6_addr)) &=
228+
(UINT128_MAX >> ifr->ifr6_prefixlen);
229+
230+
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
231+
addr6->ifaddrs.ifa_flags = ifr->ifr_flags;
232+
}
233+
234+
bzero(&addr6->bstaddr, sizeof(struct sockaddr_in6));
235+
addr6->ifaddrs.ifa_next = res;
236+
res = (struct ifaddrs *)addr6;
101237
}
102-
addr->ifaddrs.ifa_next = res;
103-
res = (struct ifaddrs *)addr;
104238
}
105239
}
106240
*out_ifpp = res;

libc/sock/struct/ifreq.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef COSMOPOLITAN_LIBC_SOCK_STRUCT_IFREQ_H_
22
#define COSMOPOLITAN_LIBC_SOCK_STRUCT_IFREQ_H_
33
#include "libc/sock/struct/sockaddr.h"
4+
#include "libc/sock/struct/sockaddr6.h"
45
COSMOPOLITAN_C_START_
56

67
#define IF_NAMESIZE 16
@@ -11,6 +12,14 @@ struct ifreq {
1112
char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */
1213
} ifr_ifrn;
1314
union {
15+
struct {
16+
uint16_t sa_family;
17+
uint16_t ifr6_ifindex; /* Interface index */
18+
uint16_t ifr6_flags; /* Flags */
19+
uint8_t ifr6_scope; /* Addr scope */
20+
uint8_t ifr6_prefixlen; /* Prefix length */
21+
struct in6_addr ifr6_addr;
22+
} in6;
1423
struct sockaddr ifru_addr; /* SIOCGIFADDR */
1524
struct sockaddr ifru_dstaddr; /* SIOCGIFDSTADDR */
1625
struct sockaddr ifru_netmask; /* SIOCGIFNETMASK */
@@ -29,5 +38,11 @@ struct ifreq {
2938
#define ifr_flags ifr_ifru.ifru_flags /* flags */
3039
#define ifr_ifindex ifr_ifru.ifru_ivalue
3140

41+
#define ifr6_addr ifr_ifru.in6.ifr6_addr /* IP6 Addr */
42+
#define ifr6_scope ifr_ifru.in6.ifr6_scope /* IP6 Addr scope */
43+
#define ifr6_prefixlen ifr_ifru.in6.ifr6_prefixlen /* IP6 Prefix length */
44+
#define ifr6_ifindex ifr_ifru.in6.ifr6_ifindex /* IP6 If index */
45+
#define ifr6_flags ifr_ifru.in6.ifr6_flags /* IP6 If flags */
46+
3247
COSMOPOLITAN_C_END_
3348
#endif /* COSMOPOLITAN_LIBC_SOCK_STRUCT_IFREQ_H_ */

tool/viz/getifaddrs.c

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
eth0
3434
addr: 10.10.10.237
3535
netmask: 255.255.255.0
36-
broadcast: 255.255.255.0
36+
broadcast: 10.10.10.255
3737
flags: IFF_UP IFF_BROADCAST IFF_MULTICAST IFF_RUNNING
3838
3939
lo
@@ -74,13 +74,87 @@ int main(int argc, char *argv[]) {
7474
tinyprint(1, "netmask: ", buf, "\n", NULL);
7575
}
7676
if ((ifa->ifa_flags & IFF_BROADCAST) &&
77-
sockaddr2str(ifa->ifa_netmask, buf, sizeof(buf))) {
77+
sockaddr2str(ifa->ifa_broadaddr, buf, sizeof(buf))) {
7878
tinyprint(1, "broadcast: ", buf, "\n", NULL);
7979
} else if ((ifa->ifa_flags & IFF_POINTOPOINT) &&
8080
sockaddr2str(ifa->ifa_dstaddr, buf, sizeof(buf))) {
8181
tinyprint(1, "dstaddr: ", buf, "\n", NULL);
8282
}
8383

84+
if (ifa->ifa_addr->sa_family == AF_INET6) {
85+
int scope = ((int *)ifa->ifa_data)[0];
86+
int aflags = ((int *)ifa->ifa_data)[1];
87+
// #define IPV6_ADDR_LOOPBACK 0x0010U
88+
// #define IPV6_ADDR_LINKLOCAL 0x0020U
89+
// #define IPV6_ADDR_SITELOCAL 0x0040U
90+
91+
// #define IFA_F_TEMPORARY 0x01
92+
// #define IFA_F_NODAD 0x02
93+
// #define IFA_F_OPTIMISTIC 0x04
94+
// #define IFA_F_DADFAILED 0x08
95+
// #define IFA_F_HOMEADDRESS 0x10
96+
// #define IFA_F_DEPRECATED 0x20
97+
// #define IFA_F_TENTATIVE 0x40
98+
// #define IFA_F_PERMANENT 0x80
99+
// #define IFA_F_MANAGETEMPADDR 0x100
100+
// #define IFA_F_NOPREFIXROUTE 0x200
101+
// #define IFA_F_MCAUTOJOIN 0x400
102+
// #define IFA_F_STABLE_PRIVACY 0x800
103+
tinyprint(1, "scope:", NULL);
104+
if (scope == 0x10) {
105+
tinyprint(1, " loopback", NULL);
106+
}
107+
if (scope == 0x20) {
108+
tinyprint(1, " linklocal", NULL);
109+
}
110+
if (scope == 0x40) {
111+
tinyprint(1, " sitelocal", NULL);
112+
}
113+
if (scope == 0x00) {
114+
tinyprint(1, " global", NULL);
115+
}
116+
tinyprint(1, "\n", NULL);
117+
118+
tinyprint(1, "addr flags:", NULL);
119+
if (aflags & 0x01) {
120+
tinyprint(1, " temporary", NULL);
121+
}
122+
if (aflags & 0x02) {
123+
tinyprint(1, " nodad", NULL);
124+
}
125+
if (aflags & 0x04) {
126+
tinyprint(1, " optimistic", NULL);
127+
}
128+
if (aflags & 0x08) {
129+
tinyprint(1, " dadfailed", NULL);
130+
}
131+
if (aflags & 0x10) {
132+
tinyprint(1, " homeaddress", NULL);
133+
}
134+
if (aflags & 0x20) {
135+
tinyprint(1, " deprecated", NULL);
136+
}
137+
if (aflags & 0x40) {
138+
tinyprint(1, " tentative", NULL);
139+
}
140+
if (aflags & 0x80) {
141+
tinyprint(1, " permanent", NULL);
142+
}
143+
if (aflags & 0x100) {
144+
tinyprint(1, " managetempaddr", NULL);
145+
}
146+
if (aflags & 0x200) {
147+
tinyprint(1, " noprefixroute", NULL);
148+
}
149+
if (aflags & 0x400) {
150+
tinyprint(1, " mcautojoin", NULL);
151+
}
152+
if (aflags & 0x800) {
153+
tinyprint(1, " stable_privacy", NULL);
154+
}
155+
tinyprint(1, "\n", NULL);
156+
}
157+
84158
tinyprint(1, "flags:", NULL);
85159
if (ifa->ifa_flags & IFF_UP) {
86160
tinyprint(1, " IFF_UP", NULL);

0 commit comments

Comments
 (0)