1 /* 2 * Copyright 2015, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Adrien Destugues, pulkomandy@pulkomandy.tk 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 11 #include <algorithm> 12 #include <new> 13 14 #include <errno.h> 15 #include <net/if.h> 16 #include <netinet/in.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <sys/socket.h> 20 #include <sys/sockio.h> 21 #include <unistd.h> 22 23 #include <AutoDeleter.h> 24 25 #include <ifaddrs.h> 26 27 28 static sockaddr* 29 copy_address(const sockaddr& address) 30 { 31 if (address.sa_family == AF_UNSPEC) 32 return NULL; 33 34 sockaddr_storage* copy = new (std::nothrow) sockaddr_storage; 35 if (copy == NULL) 36 return NULL; 37 38 size_t length = std::min(sizeof(sockaddr_storage), (size_t)address.sa_len); 39 switch (address.sa_family) { 40 case AF_INET: 41 length = sizeof(sockaddr_in); 42 break; 43 case AF_INET6: 44 length = sizeof(sockaddr_in6); 45 break; 46 } 47 memcpy(copy, &address, length); 48 copy->ss_len = length; 49 return (sockaddr*)copy; 50 } 51 52 53 static int 54 _getifaddrs(int domain, char* buffer, size_t len, struct ifaddrs** previous) 55 { 56 int socket = ::socket(domain, SOCK_DGRAM, 0); 57 if (socket < 0) 58 return -1; 59 FileDescriptorCloser closer(socket); 60 61 // Get interfaces configuration 62 ifconf config; 63 config.ifc_buf = buffer; 64 config.ifc_len = len; 65 if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0) 66 return -1; 67 68 ifreq* interfaces = (ifreq*)buffer; 69 ifreq* end = (ifreq*)(buffer + config.ifc_len); 70 71 while (interfaces < end) { 72 struct ifaddrs* current = new(std::nothrow) ifaddrs(); 73 if (current == NULL) { 74 errno = B_NO_MEMORY; 75 return -1; 76 } 77 78 // Chain this interface with the next one 79 current->ifa_next = *previous; 80 *previous = current; 81 82 current->ifa_name = strdup(interfaces[0].ifr_name); 83 current->ifa_addr = copy_address(interfaces[0].ifr_addr); 84 current->ifa_netmask = NULL; 85 current->ifa_dstaddr = NULL; 86 current->ifa_data = NULL; 87 88 ifreq request; 89 strlcpy(request.ifr_name, interfaces[0].ifr_name, IF_NAMESIZE); 90 91 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) 92 current->ifa_flags = request.ifr_flags; 93 if (ioctl(socket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) 94 == 0) { 95 current->ifa_netmask = copy_address(request.ifr_mask); 96 } 97 if (ioctl(socket, SIOCGIFDSTADDR, &request, sizeof(struct ifreq)) 98 == 0) { 99 current->ifa_dstaddr = copy_address(request.ifr_dstaddr); 100 } 101 102 // Move on to next interface 103 interfaces = (ifreq*)((uint8_t*)interfaces 104 + _SIZEOF_ADDR_IFREQ(interfaces[0])); 105 } 106 107 return 0; 108 } 109 110 111 /*! Returns a chained list of all interfaces. */ 112 int 113 getifaddrs(struct ifaddrs** _ifaddrs) 114 { 115 if (_ifaddrs == NULL) { 116 errno = B_BAD_VALUE; 117 return -1; 118 } 119 120 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 121 if (socket < 0) 122 return -1; 123 124 FileDescriptorCloser closer(socket); 125 126 // Get interface count 127 ifconf config; 128 config.ifc_len = sizeof(config.ifc_value); 129 if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0) 130 return -1; 131 132 socket = -1; 133 closer.Unset(); 134 135 size_t count = (size_t)config.ifc_value; 136 if (count == 0) { 137 // No interfaces found 138 *_ifaddrs = NULL; 139 return 0; 140 } 141 142 // Allocate a buffer for ifreqs for all interfaces 143 size_t buflen = count * sizeof(struct ifreq); 144 char* buffer = (char*)malloc(buflen); 145 if (buffer == NULL) { 146 errno = B_NO_MEMORY; 147 return -1; 148 } 149 150 MemoryDeleter deleter(buffer); 151 152 struct ifaddrs* previous = NULL; 153 int serrno = errno; 154 if (_getifaddrs(AF_INET, buffer, buflen, &previous) < 0 && 155 errno != B_UNSUPPORTED) { 156 freeifaddrs(previous); 157 return -1; 158 } 159 if (_getifaddrs(AF_INET6, buffer, buflen, &previous) < 0 && 160 errno != B_UNSUPPORTED) { 161 freeifaddrs(previous); 162 return -1; 163 } 164 if (_getifaddrs(AF_LINK, buffer, buflen, &previous) < 0 && 165 errno != B_UNSUPPORTED) { 166 freeifaddrs(previous); 167 return -1; 168 } 169 if (previous == NULL) 170 return -1; 171 errno = serrno; 172 173 *_ifaddrs = previous; 174 return 0; 175 } 176 177 178 void 179 freeifaddrs(struct ifaddrs* ifa) 180 { 181 while (ifa != NULL) { 182 free((void*)ifa->ifa_name); 183 delete ifa->ifa_addr; 184 delete ifa->ifa_netmask; 185 delete ifa->ifa_dstaddr; 186 187 struct ifaddrs* next = ifa->ifa_next; 188 delete ifa; 189 ifa = next; 190 } 191 } 192 193