getifaddrs.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. typedef union {
  15. struct sockaddr_in6 v6;
  16. struct sockaddr_in v4;
  17. } soa;
  18. typedef struct ifaddrs_storage {
  19. struct ifaddrs ifa;
  20. soa addr;
  21. soa netmask;
  22. soa dst;
  23. char name[IFNAMSIZ+1];
  24. } stor;
  25. #define next ifa.ifa_next
  26. static stor* list_add(stor** list, stor** head, char* ifname)
  27. {
  28. stor* curr = calloc(1, sizeof(stor));
  29. if(curr) {
  30. strcpy(curr->name, ifname);
  31. curr->ifa.ifa_name = curr->name;
  32. if(*head) (*head)->next = (struct ifaddrs*) curr;
  33. *head = curr;
  34. if(!*list) *list = curr;
  35. }
  36. return curr;
  37. }
  38. void freeifaddrs(struct ifaddrs *ifp)
  39. {
  40. stor *head = (stor *) ifp;
  41. while(head) {
  42. void *p = head;
  43. head = (stor *) head->next;
  44. free(p);
  45. }
  46. }
  47. static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa)
  48. {
  49. unsigned char* hb = sa->sin6_addr.s6_addr;
  50. unsigned onebytes = prefix_length / 8;
  51. unsigned bits = prefix_length % 8;
  52. unsigned nullbytes = 16 - onebytes;
  53. memset(hb, -1, onebytes);
  54. memset(hb+onebytes, 0, nullbytes);
  55. if(bits) {
  56. unsigned char x = -1;
  57. x <<= 8 - bits;
  58. hb[onebytes] = x;
  59. }
  60. }
  61. static void dealwithipv6(stor **list, stor** head)
  62. {
  63. FILE* f = fopen("/proc/net/if_inet6", "rbe");
  64. /* 00000000000000000000000000000001 01 80 10 80 lo
  65. A B C D E F
  66. all numbers in hex
  67. A = addr B=netlink device#, C=prefix length,
  68. D = scope value (ipv6.h) E = interface flags (rnetlink.h, addrconf.c)
  69. F = if name */
  70. char v6conv[32 + 7 + 1], *v6;
  71. char *line, linebuf[512];
  72. if(!f) return;
  73. while((line = fgets(linebuf, sizeof linebuf, f))) {
  74. v6 = v6conv;
  75. size_t i = 0;
  76. for(; i < 8; i++) {
  77. memcpy(v6, line, 4);
  78. v6+=4;
  79. *v6++=':';
  80. line+=4;
  81. }
  82. --v6; *v6 = 0;
  83. line++;
  84. unsigned b, c, d, e;
  85. char name[IFNAMSIZ+1];
  86. if(5 == sscanf(line, "%x %x %x %x %s", &b, &c, &d, &e, name)) {
  87. struct sockaddr_in6 sa = {0};
  88. if(1 == inet_pton(AF_INET6, v6conv, &sa.sin6_addr)) {
  89. sa.sin6_family = AF_INET6;
  90. stor* curr = list_add(list, head, name);
  91. if(!curr) goto out;
  92. curr->addr.v6 = sa;
  93. curr->ifa.ifa_addr = (struct sockaddr*) &curr->addr;
  94. ipv6netmask(c, &sa);
  95. curr->netmask.v6 = sa;
  96. curr->ifa.ifa_netmask = (struct sockaddr*) &curr->netmask;
  97. /* find ipv4 struct with the same interface name to copy flags */
  98. stor* scan = *list;
  99. for(;scan && strcmp(name, scan->name);scan=(stor*)scan->next);
  100. if(scan) curr->ifa.ifa_flags = scan->ifa.ifa_flags;
  101. else curr->ifa.ifa_flags = 0;
  102. } else errno = 0;
  103. }
  104. }
  105. out:
  106. fclose(f);
  107. }
  108. int getifaddrs(struct ifaddrs **ifap)
  109. {
  110. stor *list = 0, *head = 0;
  111. struct if_nameindex* ii = if_nameindex();
  112. if(!ii) return -1;
  113. size_t i;
  114. for(i = 0; ii[i].if_index || ii[i].if_name; i++) {
  115. stor* curr = list_add(&list, &head, ii[i].if_name);
  116. if(!curr) {
  117. if_freenameindex(ii);
  118. goto err2;
  119. }
  120. }
  121. if_freenameindex(ii);
  122. int sock = socket(PF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP);
  123. if(sock == -1) goto err2;
  124. struct ifreq reqs[32]; /* arbitrary chosen boundary */
  125. struct ifconf conf = {.ifc_len = sizeof reqs, .ifc_req = reqs};
  126. if(-1 == ioctl(sock, SIOCGIFCONF, &conf)) goto err;
  127. size_t reqitems = conf.ifc_len / sizeof(struct ifreq);
  128. for(head = list; head; head = (stor*)head->next) {
  129. for(i = 0; i < reqitems; i++) {
  130. // get SIOCGIFADDR of active interfaces.
  131. if(!strcmp(reqs[i].ifr_name, head->name)) {
  132. head->addr.v4 = *(struct sockaddr_in*)&reqs[i].ifr_addr;
  133. head->ifa.ifa_addr = (struct sockaddr*) &head->addr;
  134. break;
  135. }
  136. }
  137. struct ifreq req;
  138. snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->name);
  139. if(-1 == ioctl(sock, SIOCGIFFLAGS, &req)) goto err;
  140. head->ifa.ifa_flags = req.ifr_flags;
  141. if(head->ifa.ifa_addr) {
  142. /* or'ing flags with IFF_LOWER_UP on active interfaces to mimic glibc */
  143. head->ifa.ifa_flags |= IFF_LOWER_UP;
  144. if(-1 == ioctl(sock, SIOCGIFNETMASK, &req)) goto err;
  145. head->netmask.v4 = *(struct sockaddr_in*)&req.ifr_netmask;
  146. head->ifa.ifa_netmask = (struct sockaddr*) &head->netmask;
  147. if(head->ifa.ifa_flags & IFF_POINTOPOINT) {
  148. if(-1 == ioctl(sock, SIOCGIFDSTADDR, &req)) goto err;
  149. head->dst.v4 = *(struct sockaddr_in*)&req.ifr_dstaddr;
  150. } else {
  151. if(-1 == ioctl(sock, SIOCGIFBRDADDR, &req)) goto err;
  152. head->dst.v4 = *(struct sockaddr_in*)&req.ifr_broadaddr;
  153. }
  154. head->ifa.ifa_ifu.ifu_dstaddr = (struct sockaddr*) &head->dst;
  155. }
  156. }
  157. close(sock);
  158. void* last = 0;
  159. for(head = list; head; head=(stor*)head->next) last=head;
  160. head = last;
  161. dealwithipv6(&list, &head);
  162. *ifap = (struct ifaddrs*) list;
  163. return 0;
  164. err:
  165. close(sock);
  166. err2:
  167. freeifaddrs((struct ifaddrs*) list);
  168. return -1;
  169. }