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 FileDescriptorCloser socket (::socket(domain, SOCK_DGRAM, 0)); 57 if (!socket.IsSet()) 58 return -1; 59 60 // Get interfaces configuration 61 ifconf config; 62 config.ifc_buf = buffer; 63 config.ifc_len = len; 64 if (ioctl(socket.Get(), SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0) 65 return -1; 66 67 ifreq* interfaces = (ifreq*)buffer; 68 ifreq* end = (ifreq*)(buffer + config.ifc_len); 69 70 while (interfaces < end) { 71 struct ifaddrs* current = new(std::nothrow) ifaddrs(); 72 if (current == NULL) { 73 errno = B_NO_MEMORY; 74 return -1; 75 } 76 77 // Chain this interface with the next one 78 current->ifa_next = *previous; 79 *previous = current; 80 81 current->ifa_name = strdup(interfaces[0].ifr_name); 82 current->ifa_addr = copy_address(interfaces[0].ifr_addr); 83 current->ifa_netmask = NULL; 84 current->ifa_dstaddr = NULL; 85 current->ifa_data = NULL; 86 87 ifreq request; 88 strlcpy(request.ifr_name, interfaces[0].ifr_name, IF_NAMESIZE); 89 90 if (ioctl(socket.Get(), SIOCGIFFLAGS, &request, sizeof(struct ifreq)) 91 == 0) 92 current->ifa_flags = request.ifr_flags; 93 if (ioctl(socket.Get(), SIOCGIFNETMASK, &request, sizeof(struct ifreq)) 94 == 0) { 95 current->ifa_netmask = copy_address(request.ifr_mask); 96 } 97 if (ioctl(socket.Get(), 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 FileDescriptorCloser socket(::socket(AF_INET, SOCK_DGRAM, 0)); 121 if (!socket.IsSet()) 122 return -1; 123 124 // Get interface count 125 ifconf config; 126 config.ifc_len = sizeof(config.ifc_value); 127 if (ioctl(socket.Get(), SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0) 128 return -1; 129 130 socket.Unset(); 131 132 size_t count = (size_t)config.ifc_value; 133 if (count == 0) { 134 // No interfaces found 135 *_ifaddrs = NULL; 136 return 0; 137 } 138 139 // Allocate a buffer for ifreqs for all interfaces 140 size_t buflen = count * sizeof(struct ifreq); 141 ArrayDeleter<char> buffer(new(std::nothrow) char[buflen]); 142 if (!buffer.IsSet()) { 143 errno = B_NO_MEMORY; 144 return -1; 145 } 146 147 struct ifaddrs* previous = NULL; 148 int serrno = errno; 149 if (_getifaddrs(AF_INET, buffer.Get(), buflen, &previous) < 0 && 150 errno != B_UNSUPPORTED) { 151 freeifaddrs(previous); 152 return -1; 153 } 154 if (_getifaddrs(AF_INET6, buffer.Get(), buflen, &previous) < 0 && 155 errno != B_UNSUPPORTED) { 156 freeifaddrs(previous); 157 return -1; 158 } 159 if (_getifaddrs(AF_LINK, buffer.Get(), buflen, &previous) < 0 && 160 errno != B_UNSUPPORTED) { 161 freeifaddrs(previous); 162 return -1; 163 } 164 if (previous == NULL) 165 return -1; 166 errno = serrno; 167 168 *_ifaddrs = previous; 169 return 0; 170 } 171 172 173 void 174 freeifaddrs(struct ifaddrs* ifa) 175 { 176 while (ifa != NULL) { 177 free((void*)ifa->ifa_name); 178 delete ifa->ifa_addr; 179 delete ifa->ifa_netmask; 180 delete ifa->ifa_dstaddr; 181 182 struct ifaddrs* next = ifa->ifa_next; 183 delete ifa; 184 ifa = next; 185 } 186 } 187 188