1 /* (C) 2013 John Spencer. released under musl's standard MIT license. */
6 #include <net/if.h> /* IFNAMSIZ, ifreq, ifconf */
11 #include <arpa/inet.h> /* inet_pton */
13 #include <sys/ioctl.h>
15 static struct ifaddrs* list_add(struct ifaddrs** list, struct ifaddrs** head, char* ifname)
17 struct ifaddrs* curr = calloc(1, sizeof(struct ifaddrs));
19 curr->ifa_name = strdup(ifname);
25 if(*head) (*head)->ifa_next = curr;
27 if(!*list) *list = curr;
33 void freeifaddrs(struct ifaddrs *ifp)
35 struct ifaddrs *head = ifp;
39 free(head->ifa_netmask);
40 free(head->ifa_ifu.ifu_dstaddr);
43 head = head->ifa_next;
48 static struct sockaddr *sockaddr_in_dup(struct sockaddr_in *src)
50 struct sockaddr_in *nu = malloc(sizeof(struct sockaddr_in));
52 return (struct sockaddr*) nu;
55 static struct sockaddr *sockaddr_in6_dup(struct sockaddr_in6 *src)
57 struct sockaddr_in6 *nu = malloc(sizeof(struct sockaddr_in6));
59 return (struct sockaddr*) nu;
62 static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa)
64 // FIXME: left for bit-wizard rich
65 memset(&sa->sin6_addr, -1, sizeof(sa->sin6_addr));
68 static void dealwithipv6(struct ifaddrs **list, struct ifaddrs** head)
70 FILE* f = fopen("/proc/net/if_inet6", "r");
71 /* 00000000000000000000000000000001 01 80 10 80 lo
74 A = addr B=netlink device#, C=prefix length,
75 D = scope value (ipv6.h) E = interface flags (rnetlink.h, addrconf.c)
77 char v6conv[32 + 7 + 1], *v6;
78 char *line, linebuf[512];
80 while((line = fgets(linebuf, sizeof linebuf, f))) {
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 struct ifaddrs* curr = list_add(list, head, name);
99 curr->ifa_addr = sockaddr_in6_dup(&sa);
101 curr->ifa_netmask = sockaddr_in6_dup(&sa);
102 /* find ipv4 struct with the same interface name to copy flags */
103 struct ifaddrs* scan = *list;
104 for(;scan && strcmp(name, scan->ifa_name);scan=scan->ifa_next);
105 if(scan) curr->ifa_flags=scan->ifa_flags;
106 else curr->ifa_flags = 0;
114 int getifaddrs(struct ifaddrs **ifap)
116 FILE* f = fopen("/proc/net/dev", "r");
117 /* the alternative to parsing /proc.. seems to be iterating
118 through the interfaces using an index number in ifreq.ifr_ifindex
119 until we get some error code back. the kernel will fill ifr_name field
120 for valid ifindices (SIOCGIFINDEX) */
122 struct ifaddrs *list = 0, *head = 0;
124 char* line; char linebuf[512];
125 while((line = fgets(linebuf, sizeof linebuf, f))) {
126 while(isspace(*line) && *line) line++;
128 while(*line && isalnum(*line)) line++;
129 if(line > start && *line == ':') {
132 struct ifaddrs* curr = list_add(&list, &head, start);
141 int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
142 if(sock == -1) goto err2;
143 struct ifreq reqs[32]; /* arbitrary chosen boundary */
144 struct ifconf conf = {.ifc_len = sizeof reqs, .ifc_req = reqs};
145 if(-1 == ioctl(sock, SIOCGIFCONF, &conf)) goto err;
147 size_t reqitems = conf.ifc_len / sizeof(struct ifreq);
148 for(head = list; head; head=head->ifa_next) {
150 for(i = 0; i < reqitems; i++) {
151 // get SIOCGIFADDR of active interfaces.
152 if(!strcmp(reqs[i].ifr_name, head->ifa_name)) {
153 head->ifa_addr = sockaddr_in_dup((struct sockaddr_in*) &reqs[i].ifr_addr);
158 snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->ifa_name);
159 if(-1 == ioctl(sock, SIOCGIFFLAGS, &req)) goto err;
161 head->ifa_flags = req.ifr_flags;
163 /* or'ing flags with IFF_LOWER_UP on active interfaces to mimic glibc */
164 head->ifa_flags |= IFF_LOWER_UP;
165 if(-1 == ioctl(sock, SIOCGIFNETMASK, &req)) goto err;
166 head->ifa_netmask = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_netmask);
168 if(head->ifa_flags & IFF_POINTOPOINT) {
169 if(-1 == ioctl(sock, SIOCGIFDSTADDR, &req)) goto err;
170 head->ifa_ifu.ifu_dstaddr = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_dstaddr);
172 if(-1 == ioctl(sock, SIOCGIFBRDADDR, &req)) goto err;
173 head->ifa_ifu.ifu_broadaddr = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_broadaddr);
180 for(head = list; head; head=head->ifa_next) last=head;
182 dealwithipv6(&list, &head);