1 /* $FreeBSD: src/lib/libc/net/getifaddrs.c,v 1.1.2.4 2002/08/01 19:31:06 ume Exp $ */ 2 /* $DragonFly: src/lib/libc/net/getifaddrs.c,v 1.4 2004/02/03 07:34:09 dillon Exp $ */ 3 /* $KAME: getifaddrs.c,v 1.9 2001/08/20 02:31:20 itojun Exp $ */ 4 5 /* 6 * Copyright (c) 1995, 1999 7 * Berkeley Software Design, Inc. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp 28 */ 29 /* 30 * NOTE: SIOCGIFCONF case is not LP64 friendly. it also does not perform 31 * try-and-error for region size. 32 */ 33 #include <sys/param.h> 34 #include <sys/types.h> 35 #include <sys/ioctl.h> 36 #include <sys/socket.h> 37 #include <net/if.h> 38 #ifdef NET_RT_IFLIST 39 #include <net/route.h> 40 #include <sys/sysctl.h> 41 #include <net/if_dl.h> 42 #endif 43 44 #include <errno.h> 45 #include <ifaddrs.h> 46 #include <stdlib.h> 47 #include <string.h> 48 49 #if !defined(AF_LINK) 50 #define SA_LEN(sa) sizeof(struct sockaddr) 51 #endif 52 53 #if !defined(SA_LEN) 54 #define SA_LEN(sa) (sa)->sa_len 55 #endif 56 57 #define SALIGN (sizeof(long) - 1) 58 #define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1)) 59 60 #ifndef ALIGNBYTES 61 /* 62 * On systems with a routing socket, ALIGNBYTES should match the value 63 * that the kernel uses when building the messages. 64 */ 65 #define ALIGNBYTES XXX 66 #endif 67 #ifndef ALIGN 68 #define ALIGN(p) (((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES) 69 #endif 70 71 #if _BSDI_VERSION >= 199701 72 #define HAVE_IFM_DATA 73 #endif 74 75 #if _BSDI_VERSION >= 199802 76 /* ifam_data is very specific to recent versions of bsdi */ 77 #define HAVE_IFAM_DATA 78 #endif 79 80 #if defined(__NetBSD__) || defined(__OpenBSD__) || \ 81 defined(__FreeBSD__) || defined(__DragonFly__) 82 #define HAVE_IFM_DATA 83 #endif 84 85 #define MAX_SYSCTL_TRY 5 86 87 int 88 getifaddrs(struct ifaddrs **pif) 89 { 90 int icnt = 1; 91 int dcnt = 0; 92 int ncnt = 0; 93 #ifdef NET_RT_IFLIST 94 int ntry = 0; 95 int mib[6]; 96 size_t needed; 97 char *buf; 98 char *next; 99 struct ifaddrs *cif = 0; 100 char *p, *p0; 101 struct rt_msghdr *rtm; 102 struct if_msghdr *ifm; 103 struct ifa_msghdr *ifam; 104 struct sockaddr_dl *dl; 105 struct sockaddr *sa; 106 struct ifaddrs *ifa, *ift; 107 u_short idx = 0; 108 #else /* NET_RT_IFLIST */ 109 char buf[1024]; 110 int m, sock; 111 struct ifconf ifc; 112 struct ifreq *ifr; 113 struct ifreq *lifr; 114 #endif /* NET_RT_IFLIST */ 115 int i; 116 size_t len, alen; 117 char *data; 118 char *names; 119 120 #ifdef NET_RT_IFLIST 121 mib[0] = CTL_NET; 122 mib[1] = PF_ROUTE; 123 mib[2] = 0; /* protocol */ 124 mib[3] = 0; /* wildcard address family */ 125 mib[4] = NET_RT_IFLIST; 126 mib[5] = 0; /* no flags */ 127 do { 128 /* 129 * We'll try to get addresses several times in case that 130 * the number of addresses is unexpectedly increased during 131 * the two sysctl calls. This should rarely happen, but we'll 132 * try to do our best for applications that assume success of 133 * this library (which should usually be the case). 134 * Portability note: since FreeBSD does not add margin of 135 * memory at the first sysctl, the possibility of failure on 136 * the second sysctl call is a bit higher. 137 */ 138 139 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 140 return (-1); 141 if ((buf = malloc(needed)) == NULL) 142 return (-1); 143 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 144 if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { 145 free(buf); 146 return (-1); 147 } 148 free(buf); 149 buf = NULL; 150 } 151 } while (buf == NULL); 152 153 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 154 rtm = (struct rt_msghdr *)(void *)next; 155 if (rtm->rtm_version != RTM_VERSION) 156 continue; 157 switch (rtm->rtm_type) { 158 case RTM_IFINFO: 159 ifm = (struct if_msghdr *)(void *)rtm; 160 if (ifm->ifm_addrs & RTA_IFP) { 161 idx = ifm->ifm_index; 162 ++icnt; 163 dl = (struct sockaddr_dl *)(void *)(ifm + 1); 164 dcnt += SA_RLEN((struct sockaddr *)(void*)dl) + 165 ALIGNBYTES; 166 #ifdef HAVE_IFM_DATA 167 dcnt += sizeof(ifm->ifm_data); 168 #endif /* HAVE_IFM_DATA */ 169 ncnt += dl->sdl_nlen + 1; 170 } else 171 idx = 0; 172 break; 173 174 case RTM_NEWADDR: 175 ifam = (struct ifa_msghdr *)(void *)rtm; 176 if (idx && ifam->ifam_index != idx) 177 abort(); /* this cannot happen */ 178 179 #define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD) 180 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 181 break; 182 p = (char *)(void *)(ifam + 1); 183 ++icnt; 184 #ifdef HAVE_IFAM_DATA 185 dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES; 186 #endif /* HAVE_IFAM_DATA */ 187 /* Scan to look for length of address */ 188 alen = 0; 189 for (p0 = p, i = 0; i < RTAX_MAX; i++) { 190 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 191 == 0) 192 continue; 193 sa = (struct sockaddr *)(void *)p; 194 len = SA_RLEN(sa); 195 if (i == RTAX_IFA) { 196 alen = len; 197 break; 198 } 199 p += len; 200 } 201 for (p = p0, i = 0; i < RTAX_MAX; i++) { 202 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 203 == 0) 204 continue; 205 sa = (struct sockaddr *)(void *)p; 206 len = SA_RLEN(sa); 207 if (i == RTAX_NETMASK && SA_LEN(sa) == 0) 208 dcnt += alen; 209 else 210 dcnt += len; 211 p += len; 212 } 213 break; 214 } 215 } 216 #else /* NET_RT_IFLIST */ 217 ifc.ifc_buf = buf; 218 ifc.ifc_len = sizeof(buf); 219 220 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 221 return (-1); 222 i = ioctl(sock, SIOCGIFCONF, (char *)&ifc); 223 close(sock); 224 if (i < 0) 225 return (-1); 226 227 ifr = ifc.ifc_req; 228 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; 229 230 while (ifr < lifr) { 231 struct sockaddr *sa; 232 233 sa = &ifr->ifr_addr; 234 ++icnt; 235 dcnt += SA_RLEN(sa); 236 ncnt += sizeof(ifr->ifr_name) + 1; 237 238 if (SA_LEN(sa) < sizeof(*sa)) 239 ifr = (struct ifreq *)(((char *)sa) + sizeof(*sa)); 240 else 241 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); 242 } 243 #endif /* NET_RT_IFLIST */ 244 245 if (icnt + dcnt + ncnt == 1) { 246 *pif = NULL; 247 free(buf); 248 return (0); 249 } 250 data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt); 251 if (data == NULL) { 252 free(buf); 253 return(-1); 254 } 255 256 ifa = (struct ifaddrs *)(void *)data; 257 data += sizeof(struct ifaddrs) * icnt; 258 names = data + dcnt; 259 260 memset(ifa, 0, sizeof(struct ifaddrs) * icnt); 261 ift = ifa; 262 263 #ifdef NET_RT_IFLIST 264 idx = 0; 265 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 266 rtm = (struct rt_msghdr *)(void *)next; 267 if (rtm->rtm_version != RTM_VERSION) 268 continue; 269 switch (rtm->rtm_type) { 270 case RTM_IFINFO: 271 ifm = (struct if_msghdr *)(void *)rtm; 272 if (ifm->ifm_addrs & RTA_IFP) { 273 idx = ifm->ifm_index; 274 dl = (struct sockaddr_dl *)(void *)(ifm + 1); 275 276 cif = ift; 277 ift->ifa_name = names; 278 ift->ifa_flags = (int)ifm->ifm_flags; 279 memcpy(names, dl->sdl_data, 280 (size_t)dl->sdl_nlen); 281 names[dl->sdl_nlen] = 0; 282 names += dl->sdl_nlen + 1; 283 284 ift->ifa_addr = (struct sockaddr *)(void *)data; 285 memcpy(data, dl, 286 (size_t)SA_LEN((struct sockaddr *) 287 (void *)dl)); 288 data += SA_RLEN((struct sockaddr *)(void *)dl); 289 290 #ifdef HAVE_IFM_DATA 291 /* ifm_data needs to be aligned */ 292 ift->ifa_data = data = (void *)ALIGN(data); 293 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data)); 294 data += sizeof(ifm->ifm_data); 295 #else /* HAVE_IFM_DATA */ 296 ift->ifa_data = NULL; 297 #endif /* HAVE_IFM_DATA */ 298 299 ift = (ift->ifa_next = ift + 1); 300 } else 301 idx = 0; 302 break; 303 304 case RTM_NEWADDR: 305 ifam = (struct ifa_msghdr *)(void *)rtm; 306 if (idx && ifam->ifam_index != idx) 307 abort(); /* this cannot happen */ 308 309 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 310 break; 311 ift->ifa_name = cif->ifa_name; 312 ift->ifa_flags = cif->ifa_flags; 313 ift->ifa_data = NULL; 314 p = (char *)(void *)(ifam + 1); 315 /* Scan to look for length of address */ 316 alen = 0; 317 for (p0 = p, i = 0; i < RTAX_MAX; i++) { 318 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 319 == 0) 320 continue; 321 sa = (struct sockaddr *)(void *)p; 322 len = SA_RLEN(sa); 323 if (i == RTAX_IFA) { 324 alen = len; 325 break; 326 } 327 p += len; 328 } 329 for (p = p0, i = 0; i < RTAX_MAX; i++) { 330 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 331 == 0) 332 continue; 333 sa = (struct sockaddr *)(void *)p; 334 len = SA_RLEN(sa); 335 switch (i) { 336 case RTAX_IFA: 337 ift->ifa_addr = 338 (struct sockaddr *)(void *)data; 339 memcpy(data, p, len); 340 data += len; 341 break; 342 343 case RTAX_NETMASK: 344 ift->ifa_netmask = 345 (struct sockaddr *)(void *)data; 346 if (SA_LEN(sa) == 0) { 347 memset(data, 0, alen); 348 data += alen; 349 break; 350 } 351 memcpy(data, p, len); 352 data += len; 353 break; 354 355 case RTAX_BRD: 356 ift->ifa_broadaddr = 357 (struct sockaddr *)(void *)data; 358 memcpy(data, p, len); 359 data += len; 360 break; 361 } 362 p += len; 363 } 364 365 #ifdef HAVE_IFAM_DATA 366 /* ifam_data needs to be aligned */ 367 ift->ifa_data = data = (void *)ALIGN(data); 368 memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data)); 369 data += sizeof(ifam->ifam_data); 370 #endif /* HAVE_IFAM_DATA */ 371 372 ift = (ift->ifa_next = ift + 1); 373 break; 374 } 375 } 376 377 free(buf); 378 #else /* NET_RT_IFLIST */ 379 ifr = ifc.ifc_req; 380 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; 381 382 while (ifr < lifr) { 383 struct sockaddr *sa; 384 385 ift->ifa_name = names; 386 names[sizeof(ifr->ifr_name)] = 0; 387 strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name)); 388 while (*names++) 389 ; 390 391 ift->ifa_addr = (struct sockaddr *)data; 392 sa = &ifr->ifr_addr; 393 memcpy(data, sa, SA_LEN(sa)); 394 data += SA_RLEN(sa); 395 396 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); 397 ift = (ift->ifa_next = ift + 1); 398 } 399 #endif /* NET_RT_IFLIST */ 400 if (--ift >= ifa) { 401 ift->ifa_next = NULL; 402 *pif = ifa; 403 } else { 404 *pif = NULL; 405 free(ifa); 406 } 407 return (0); 408 } 409 410 void 411 freeifaddrs(struct ifaddrs *ifp) 412 { 413 414 free(ifp); 415 } 416