1 /* 2 * Copyright (c) 2000 - 2001 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifdef HAVE_CONFIG_H 35 #include <config.h> 36 RCSID("$Id: getifaddrs.c,v 1.7 2001/11/30 03:27:30 assar Exp $"); 37 #endif 38 #include "roken.h" 39 40 #ifdef __osf__ 41 /* hate */ 42 struct rtentry; 43 struct mbuf; 44 #endif 45 #ifdef HAVE_NET_IF_H 46 #include <net/if.h> 47 #endif 48 49 #ifdef HAVE_SYS_SOCKIO_H 50 #include <sys/sockio.h> 51 #endif /* HAVE_SYS_SOCKIO_H */ 52 53 #ifdef HAVE_NETINET_IN6_VAR_H 54 #include <netinet/in6_var.h> 55 #endif /* HAVE_NETINET_IN6_VAR_H */ 56 57 #include <ifaddrs.h> 58 59 static int 60 getifaddrs2(struct ifaddrs **ifap, 61 int af, int siocgifconf, int siocgifflags, 62 size_t ifreq_sz) 63 { 64 int ret; 65 int fd; 66 size_t buf_size; 67 char *buf; 68 struct ifconf ifconf; 69 char *p; 70 size_t sz; 71 struct sockaddr sa_zero; 72 struct ifreq *ifr; 73 struct ifaddrs *start = NULL, **end = &start; 74 75 buf = NULL; 76 77 memset (&sa_zero, 0, sizeof(sa_zero)); 78 fd = socket(af, SOCK_DGRAM, 0); 79 if (fd < 0) 80 return -1; 81 82 buf_size = 8192; 83 for (;;) { 84 buf = calloc(1, buf_size); 85 if (buf == NULL) { 86 ret = ENOMEM; 87 goto error_out; 88 } 89 ifconf.ifc_len = buf_size; 90 ifconf.ifc_buf = buf; 91 92 /* 93 * Solaris returns EINVAL when the buffer is too small. 94 */ 95 if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) { 96 ret = errno; 97 goto error_out; 98 } 99 /* 100 * Can the difference between a full and a overfull buf 101 * be determined? 102 */ 103 104 if (ifconf.ifc_len < buf_size) 105 break; 106 free (buf); 107 buf_size *= 2; 108 } 109 110 for (p = ifconf.ifc_buf; 111 p < ifconf.ifc_buf + ifconf.ifc_len; 112 p += sz) { 113 struct ifreq ifreq; 114 struct sockaddr *sa; 115 size_t salen; 116 117 ifr = (struct ifreq *)p; 118 sa = &ifr->ifr_addr; 119 120 sz = ifreq_sz; 121 salen = sizeof(struct sockaddr); 122 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 123 salen = sa->sa_len; 124 sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len); 125 #endif 126 #ifdef SA_LEN 127 salen = SA_LEN(sa); 128 sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa)); 129 #endif 130 memset (&ifreq, 0, sizeof(ifreq)); 131 memcpy (ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name)); 132 133 if (ioctl(fd, siocgifflags, &ifreq) < 0) { 134 ret = errno; 135 goto error_out; 136 } 137 138 *end = malloc(sizeof(**end)); 139 if (*end == NULL) { 140 ret = ENOMEM; 141 goto error_out; 142 } 143 144 (*end)->ifa_next = NULL; 145 (*end)->ifa_name = strdup(ifr->ifr_name); 146 (*end)->ifa_flags = ifreq.ifr_flags; 147 (*end)->ifa_addr = malloc(salen); 148 memcpy((*end)->ifa_addr, sa, salen); 149 (*end)->ifa_netmask = NULL; 150 151 #if 0 152 /* fix these when we actually need them */ 153 if(ifreq.ifr_flags & IFF_BROADCAST) { 154 (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr)); 155 memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr, 156 sizeof(ifr->ifr_broadaddr)); 157 } else if(ifreq.ifr_flags & IFF_POINTOPOINT) { 158 (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr)); 159 memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr, 160 sizeof(ifr->ifr_dstaddr)); 161 } else 162 (*end)->ifa_dstaddr = NULL; 163 #else 164 (*end)->ifa_dstaddr = NULL; 165 #endif 166 167 (*end)->ifa_data = NULL; 168 169 end = &(*end)->ifa_next; 170 171 } 172 *ifap = start; 173 close(fd); 174 free(buf); 175 return 0; 176 error_out: 177 freeifaddrs(start); 178 close(fd); 179 free(buf); 180 errno = ret; 181 return -1; 182 } 183 184 #if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) 185 static int 186 getlifaddrs2(struct ifaddrs **ifap, 187 int af, int siocgifconf, int siocgifflags, 188 size_t ifreq_sz) 189 { 190 int ret; 191 int fd; 192 size_t buf_size; 193 char *buf; 194 struct lifconf ifconf; 195 char *p; 196 size_t sz; 197 struct sockaddr sa_zero; 198 struct lifreq *ifr; 199 struct ifaddrs *start = NULL, **end = &start; 200 201 buf = NULL; 202 203 memset (&sa_zero, 0, sizeof(sa_zero)); 204 fd = socket(af, SOCK_DGRAM, 0); 205 if (fd < 0) 206 return -1; 207 208 buf_size = 8192; 209 for (;;) { 210 buf = calloc(1, buf_size); 211 if (buf == NULL) { 212 ret = ENOMEM; 213 goto error_out; 214 } 215 ifconf.lifc_family = AF_UNSPEC; 216 ifconf.lifc_flags = 0; 217 ifconf.lifc_len = buf_size; 218 ifconf.lifc_buf = buf; 219 220 /* 221 * Solaris returns EINVAL when the buffer is too small. 222 */ 223 if (ioctl (fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) { 224 ret = errno; 225 goto error_out; 226 } 227 /* 228 * Can the difference between a full and a overfull buf 229 * be determined? 230 */ 231 232 if (ifconf.lifc_len < buf_size) 233 break; 234 free (buf); 235 buf_size *= 2; 236 } 237 238 for (p = ifconf.lifc_buf; 239 p < ifconf.lifc_buf + ifconf.lifc_len; 240 p += sz) { 241 struct lifreq ifreq; 242 struct sockaddr_storage *sa; 243 size_t salen; 244 245 ifr = (struct lifreq *)p; 246 sa = &ifr->lifr_addr; 247 248 sz = ifreq_sz; 249 salen = sizeof(struct sockaddr_storage); 250 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 251 salen = sa->sa_len; 252 sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len); 253 #endif 254 #ifdef SA_LEN 255 salen = SA_LEN(sa); 256 sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa)); 257 #endif 258 memset (&ifreq, 0, sizeof(ifreq)); 259 memcpy (ifreq.lifr_name, ifr->lifr_name, sizeof(ifr->lifr_name)); 260 261 if (ioctl(fd, siocgifflags, &ifreq) < 0) { 262 ret = errno; 263 goto error_out; 264 } 265 266 *end = malloc(sizeof(**end)); 267 268 (*end)->ifa_next = NULL; 269 (*end)->ifa_name = strdup(ifr->lifr_name); 270 (*end)->ifa_flags = ifreq.lifr_flags; 271 (*end)->ifa_addr = malloc(salen); 272 memcpy((*end)->ifa_addr, sa, salen); 273 (*end)->ifa_netmask = NULL; 274 275 #if 0 276 /* fix these when we actually need them */ 277 if(ifreq.ifr_flags & IFF_BROADCAST) { 278 (*end)->ifa_broadaddr = malloc(sizeof(ifr->ifr_broadaddr)); 279 memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr, 280 sizeof(ifr->ifr_broadaddr)); 281 } else if(ifreq.ifr_flags & IFF_POINTOPOINT) { 282 (*end)->ifa_dstaddr = malloc(sizeof(ifr->ifr_dstaddr)); 283 memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr, 284 sizeof(ifr->ifr_dstaddr)); 285 } else 286 (*end)->ifa_dstaddr = NULL; 287 #else 288 (*end)->ifa_dstaddr = NULL; 289 #endif 290 291 (*end)->ifa_data = NULL; 292 293 end = &(*end)->ifa_next; 294 295 } 296 *ifap = start; 297 close(fd); 298 free(buf); 299 return 0; 300 error_out: 301 freeifaddrs(start); 302 close(fd); 303 free(buf); 304 errno = ret; 305 return -1; 306 } 307 #endif /* defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) */ 308 309 int 310 getifaddrs(struct ifaddrs **ifap) 311 { 312 int ret = -1; 313 errno = ENXIO; 314 #if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS) 315 if (ret) 316 ret = getifaddrs2 (ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS, 317 sizeof(struct in6_ifreq)); 318 #endif 319 #if defined(HAVE_IPV6) && defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) 320 if (ret) 321 ret = getlifaddrs2 (ifap, AF_INET6, SIOCGLIFCONF, SIOCGLIFFLAGS, 322 sizeof(struct lifreq)); 323 #endif 324 #if defined(HAVE_IPV6) && defined(SIOCGIFCONF) 325 if (ret) 326 ret = getifaddrs2 (ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS, 327 sizeof(struct ifreq)); 328 #endif 329 #if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS) 330 if (ret) 331 ret = getifaddrs2 (ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, 332 sizeof(struct ifreq)); 333 #endif 334 return ret; 335 } 336 337 void 338 freeifaddrs(struct ifaddrs *ifp) 339 { 340 struct ifaddrs *p, *q; 341 342 for(p = ifp; p; ) { 343 free(p->ifa_name); 344 if(p->ifa_addr) 345 free(p->ifa_addr); 346 if(p->ifa_dstaddr) 347 free(p->ifa_dstaddr); 348 if(p->ifa_netmask) 349 free(p->ifa_netmask); 350 if(p->ifa_data) 351 free(p->ifa_data); 352 q = p; 353 p = p->ifa_next; 354 free(q); 355 } 356 } 357 358 #ifdef TEST 359 360 void 361 print_addr(const char *s, struct sockaddr *sa) 362 { 363 int i; 364 printf(" %s=%d/", s, sa->sa_family); 365 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 366 for(i = 0; i < sa->sa_len - ((long)sa->sa_data - (long)&sa->sa_family); i++) 367 printf("%02x", ((unsigned char*)sa->sa_data)[i]); 368 #else 369 for(i = 0; i < sizeof(sa->sa_data); i++) 370 printf("%02x", ((unsigned char*)sa->sa_data)[i]); 371 #endif 372 printf("\n"); 373 } 374 375 void 376 print_ifaddrs(struct ifaddrs *x) 377 { 378 struct ifaddrs *p; 379 380 for(p = x; p; p = p->ifa_next) { 381 printf("%s\n", p->ifa_name); 382 printf(" flags=%x\n", p->ifa_flags); 383 if(p->ifa_addr) 384 print_addr("addr", p->ifa_addr); 385 if(p->ifa_dstaddr) 386 print_addr("dstaddr", p->ifa_dstaddr); 387 if(p->ifa_netmask) 388 print_addr("netmask", p->ifa_netmask); 389 printf(" %p\n", p->ifa_data); 390 } 391 } 392 393 int 394 main() 395 { 396 struct ifaddrs *a = NULL, *b; 397 getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, sizeof(struct ifreq)); 398 print_ifaddrs(a); 399 printf("---\n"); 400 getifaddrs(&b); 401 print_ifaddrs(b); 402 return 0; 403 } 404 #endif 405