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