include cleanups: remove unused headers and add feature test macros
[musl.git] / src / network / getifaddrs.c
blob5a94cc7c4496fe4edda2938dc672c9a3301d0552
1 /* (C) 2013 John Spencer. released under musl's standard MIT license. */
2 #undef _GNU_SOURCE
3 #define _GNU_SOURCE
4 #include <ifaddrs.h>
5 #include <stdlib.h>
6 #include <net/if.h> /* IFNAMSIZ, ifreq, ifconf */
7 #include <stdio.h>
8 #include <ctype.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <arpa/inet.h> /* inet_pton */
12 #include <unistd.h>
13 #include <sys/ioctl.h>
14 #include <sys/socket.h>
16 typedef union {
17 struct sockaddr_in6 v6;
18 struct sockaddr_in v4;
19 } soa;
21 typedef struct ifaddrs_storage {
22 struct ifaddrs ifa;
23 soa addr;
24 soa netmask;
25 soa dst;
26 char name[IFNAMSIZ+1];
27 } stor;
28 #define next ifa.ifa_next
30 static stor* list_add(stor** list, stor** head, char* ifname)
32 stor* curr = calloc(1, sizeof(stor));
33 if(curr) {
34 strcpy(curr->name, ifname);
35 curr->ifa.ifa_name = curr->name;
36 if(*head) (*head)->next = (struct ifaddrs*) curr;
37 *head = curr;
38 if(!*list) *list = curr;
40 return curr;
43 void freeifaddrs(struct ifaddrs *ifp)
45 stor *head = (stor *) ifp;
46 while(head) {
47 void *p = head;
48 head = (stor *) head->next;
49 free(p);
53 static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa)
55 unsigned char* hb = sa->sin6_addr.s6_addr;
56 unsigned onebytes = prefix_length / 8;
57 unsigned bits = prefix_length % 8;
58 unsigned nullbytes = 16 - onebytes;
59 memset(hb, -1, onebytes);
60 memset(hb+onebytes, 0, nullbytes);
61 if(bits) {
62 unsigned char x = -1;
63 x <<= 8 - bits;
64 hb[onebytes] = x;
68 static void dealwithipv6(stor **list, stor** head)
70 FILE* f = fopen("/proc/net/if_inet6", "rbe");
71 /* 00000000000000000000000000000001 01 80 10 80 lo
72 A B C D E F
73 all numbers in hex
74 A = addr B=netlink device#, C=prefix length,
75 D = scope value (ipv6.h) E = interface flags (rnetlink.h, addrconf.c)
76 F = if name */
77 char v6conv[32 + 7 + 1], *v6;
78 char *line, linebuf[512];
79 if(!f) return;
80 while((line = fgets(linebuf, sizeof linebuf, f))) {
81 v6 = v6conv;
82 size_t i = 0;
83 for(; i < 8; i++) {
84 memcpy(v6, line, 4);
85 v6+=4;
86 *v6++=':';
87 line+=4;
89 --v6; *v6 = 0;
90 line++;
91 unsigned b, c, d, e;
92 char name[IFNAMSIZ+1];
93 if(5 == sscanf(line, "%x %x %x %x %s", &b, &c, &d, &e, name)) {
94 struct sockaddr_in6 sa = {0};
95 if(1 == inet_pton(AF_INET6, v6conv, &sa.sin6_addr)) {
96 sa.sin6_family = AF_INET6;
97 stor* curr = list_add(list, head, name);
98 if(!curr) goto out;
99 curr->addr.v6 = sa;
100 curr->ifa.ifa_addr = (struct sockaddr*) &curr->addr;
101 ipv6netmask(c, &sa);
102 curr->netmask.v6 = sa;
103 curr->ifa.ifa_netmask = (struct sockaddr*) &curr->netmask;
104 /* find ipv4 struct with the same interface name to copy flags */
105 stor* scan = *list;
106 for(;scan && strcmp(name, scan->name);scan=(stor*)scan->next);
107 if(scan) curr->ifa.ifa_flags = scan->ifa.ifa_flags;
108 else curr->ifa.ifa_flags = 0;
109 } else errno = 0;
112 out:
113 fclose(f);
116 int getifaddrs(struct ifaddrs **ifap)
118 stor *list = 0, *head = 0;
119 struct if_nameindex* ii = if_nameindex();
120 if(!ii) return -1;
121 size_t i;
122 for(i = 0; ii[i].if_index || ii[i].if_name; i++) {
123 stor* curr = list_add(&list, &head, ii[i].if_name);
124 if(!curr) {
125 if_freenameindex(ii);
126 goto err2;
129 if_freenameindex(ii);
131 int sock = socket(PF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP);
132 if(sock == -1) goto err2;
133 struct ifreq reqs[32]; /* arbitrary chosen boundary */
134 struct ifconf conf = {.ifc_len = sizeof reqs, .ifc_req = reqs};
135 if(-1 == ioctl(sock, SIOCGIFCONF, &conf)) goto err;
136 size_t reqitems = conf.ifc_len / sizeof(struct ifreq);
137 for(head = list; head; head = (stor*)head->next) {
138 for(i = 0; i < reqitems; i++) {
139 // get SIOCGIFADDR of active interfaces.
140 if(!strcmp(reqs[i].ifr_name, head->name)) {
141 head->addr.v4 = *(struct sockaddr_in*)&reqs[i].ifr_addr;
142 head->ifa.ifa_addr = (struct sockaddr*) &head->addr;
143 break;
146 struct ifreq req;
147 snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->name);
148 if(-1 == ioctl(sock, SIOCGIFFLAGS, &req)) goto err;
150 head->ifa.ifa_flags = req.ifr_flags;
151 if(head->ifa.ifa_addr) {
152 /* or'ing flags with IFF_LOWER_UP on active interfaces to mimic glibc */
153 head->ifa.ifa_flags |= IFF_LOWER_UP;
154 if(-1 == ioctl(sock, SIOCGIFNETMASK, &req)) goto err;
155 head->netmask.v4 = *(struct sockaddr_in*)&req.ifr_netmask;
156 head->ifa.ifa_netmask = (struct sockaddr*) &head->netmask;
158 if(head->ifa.ifa_flags & IFF_POINTOPOINT) {
159 if(-1 == ioctl(sock, SIOCGIFDSTADDR, &req)) goto err;
160 head->dst.v4 = *(struct sockaddr_in*)&req.ifr_dstaddr;
161 } else {
162 if(-1 == ioctl(sock, SIOCGIFBRDADDR, &req)) goto err;
163 head->dst.v4 = *(struct sockaddr_in*)&req.ifr_broadaddr;
165 head->ifa.ifa_ifu.ifu_dstaddr = (struct sockaddr*) &head->dst;
168 close(sock);
169 void* last = 0;
170 for(head = list; head; head=(stor*)head->next) last=head;
171 head = last;
172 dealwithipv6(&list, &head);
173 *ifap = (struct ifaddrs*) list;
174 return 0;
175 err:
176 close(sock);
177 err2:
178 freeifaddrs((struct ifaddrs*) list);
179 return -1;