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