1 /* $FreeBSD$ */ 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 <ifaddrs.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include "un-namespace.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__) || defined(__FreeBSD__) 81 #define HAVE_IFM_DATA 82 #endif 83 84 int 85 getifaddrs(struct ifaddrs **pif) 86 { 87 int icnt = 1; 88 int dcnt = 0; 89 int ncnt = 0; 90 #ifdef NET_RT_IFLIST 91 int mib[6]; 92 size_t needed; 93 char *buf; 94 char *next; 95 struct ifaddrs *cif = 0; 96 char *p, *p0; 97 struct rt_msghdr *rtm; 98 struct if_msghdr *ifm; 99 struct ifa_msghdr *ifam; 100 struct sockaddr_dl *dl; 101 struct sockaddr *sa; 102 struct ifaddrs *ifa, *ift; 103 u_short idx = 0; 104 #else /* NET_RT_IFLIST */ 105 char buf[1024]; 106 int m, sock; 107 struct ifconf ifc; 108 struct ifreq *ifr; 109 struct ifreq *lifr; 110 #endif /* NET_RT_IFLIST */ 111 int i; 112 size_t len, alen; 113 char *data; 114 char *names; 115 116 #ifdef NET_RT_IFLIST 117 mib[0] = CTL_NET; 118 mib[1] = PF_ROUTE; 119 mib[2] = 0; /* protocol */ 120 mib[3] = 0; /* wildcard address family */ 121 mib[4] = NET_RT_IFLIST; 122 mib[5] = 0; /* no flags */ 123 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 124 return (-1); 125 if ((buf = malloc(needed)) == NULL) 126 return (-1); 127 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 128 free(buf); 129 return (-1); 130 } 131 132 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 133 rtm = (struct rt_msghdr *)(void *)next; 134 if (rtm->rtm_version != RTM_VERSION) 135 continue; 136 switch (rtm->rtm_type) { 137 case RTM_IFINFO: 138 ifm = (struct if_msghdr *)(void *)rtm; 139 if (ifm->ifm_addrs & RTA_IFP) { 140 idx = ifm->ifm_index; 141 ++icnt; 142 dl = (struct sockaddr_dl *)(void *)(ifm + 1); 143 dcnt += SA_RLEN((struct sockaddr *)(void*)dl) + 144 ALIGNBYTES; 145 #ifdef HAVE_IFM_DATA 146 dcnt += sizeof(ifm->ifm_data); 147 #endif /* HAVE_IFM_DATA */ 148 ncnt += dl->sdl_nlen + 1; 149 } else 150 idx = 0; 151 break; 152 153 case RTM_NEWADDR: 154 ifam = (struct ifa_msghdr *)(void *)rtm; 155 if (idx && ifam->ifam_index != idx) 156 abort(); /* this cannot happen */ 157 158 #define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD) 159 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 160 break; 161 p = (char *)(void *)(ifam + 1); 162 ++icnt; 163 #ifdef HAVE_IFAM_DATA 164 dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES; 165 #endif /* HAVE_IFAM_DATA */ 166 /* Scan to look for length of address */ 167 alen = 0; 168 for (p0 = p, i = 0; i < RTAX_MAX; i++) { 169 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 170 == 0) 171 continue; 172 sa = (struct sockaddr *)(void *)p; 173 len = SA_RLEN(sa); 174 if (i == RTAX_IFA) { 175 alen = len; 176 break; 177 } 178 p += len; 179 } 180 for (p = p0, i = 0; i < RTAX_MAX; i++) { 181 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 182 == 0) 183 continue; 184 sa = (struct sockaddr *)(void *)p; 185 len = SA_RLEN(sa); 186 if (i == RTAX_NETMASK && SA_LEN(sa) == 0) 187 dcnt += alen; 188 else 189 dcnt += len; 190 p += len; 191 } 192 break; 193 } 194 } 195 #else /* NET_RT_IFLIST */ 196 ifc.ifc_buf = buf; 197 ifc.ifc_len = sizeof(buf); 198 199 if ((sock = _socket(AF_INET, SOCK_STREAM, 0)) < 0) 200 return (-1); 201 i = _ioctl(sock, SIOCGIFCONF, (char *)&ifc); 202 _close(sock); 203 if (i < 0) 204 return (-1); 205 206 ifr = ifc.ifc_req; 207 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; 208 209 while (ifr < lifr) { 210 struct sockaddr *sa; 211 212 sa = &ifr->ifr_addr; 213 ++icnt; 214 dcnt += SA_RLEN(sa); 215 ncnt += sizeof(ifr->ifr_name) + 1; 216 217 if (SA_LEN(sa) < sizeof(*sa)) 218 ifr = (struct ifreq *)(((char *)sa) + sizeof(*sa)); 219 else 220 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); 221 } 222 #endif /* NET_RT_IFLIST */ 223 224 if (icnt + dcnt + ncnt == 1) { 225 *pif = NULL; 226 free(buf); 227 return (0); 228 } 229 data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt); 230 if (data == NULL) { 231 free(buf); 232 return(-1); 233 } 234 235 ifa = (struct ifaddrs *)(void *)data; 236 data += sizeof(struct ifaddrs) * icnt; 237 names = data + dcnt; 238 239 memset(ifa, 0, sizeof(struct ifaddrs) * icnt); 240 ift = ifa; 241 242 #ifdef NET_RT_IFLIST 243 idx = 0; 244 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 245 rtm = (struct rt_msghdr *)(void *)next; 246 if (rtm->rtm_version != RTM_VERSION) 247 continue; 248 switch (rtm->rtm_type) { 249 case RTM_IFINFO: 250 ifm = (struct if_msghdr *)(void *)rtm; 251 if (ifm->ifm_addrs & RTA_IFP) { 252 idx = ifm->ifm_index; 253 dl = (struct sockaddr_dl *)(void *)(ifm + 1); 254 255 cif = ift; 256 ift->ifa_name = names; 257 ift->ifa_flags = (int)ifm->ifm_flags; 258 memcpy(names, dl->sdl_data, 259 (size_t)dl->sdl_nlen); 260 names[dl->sdl_nlen] = 0; 261 names += dl->sdl_nlen + 1; 262 263 ift->ifa_addr = (struct sockaddr *)(void *)data; 264 memcpy(data, dl, 265 (size_t)SA_LEN((struct sockaddr *) 266 (void *)dl)); 267 data += SA_RLEN((struct sockaddr *)(void *)dl); 268 269 #ifdef HAVE_IFM_DATA 270 /* ifm_data needs to be aligned */ 271 ift->ifa_data = data = (void *)ALIGN(data); 272 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data)); 273 data += sizeof(ifm->ifm_data); 274 #else /* HAVE_IFM_DATA */ 275 ift->ifa_data = NULL; 276 #endif /* HAVE_IFM_DATA */ 277 278 ift = (ift->ifa_next = ift + 1); 279 } else 280 idx = 0; 281 break; 282 283 case RTM_NEWADDR: 284 ifam = (struct ifa_msghdr *)(void *)rtm; 285 if (idx && ifam->ifam_index != idx) 286 abort(); /* this cannot happen */ 287 288 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 289 break; 290 ift->ifa_name = cif->ifa_name; 291 ift->ifa_flags = cif->ifa_flags; 292 ift->ifa_data = NULL; 293 p = (char *)(void *)(ifam + 1); 294 /* Scan to look for length of address */ 295 alen = 0; 296 for (p0 = p, i = 0; i < RTAX_MAX; i++) { 297 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 298 == 0) 299 continue; 300 sa = (struct sockaddr *)(void *)p; 301 len = SA_RLEN(sa); 302 if (i == RTAX_IFA) { 303 alen = len; 304 break; 305 } 306 p += len; 307 } 308 for (p = p0, i = 0; i < RTAX_MAX; i++) { 309 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 310 == 0) 311 continue; 312 sa = (struct sockaddr *)(void *)p; 313 len = SA_RLEN(sa); 314 switch (i) { 315 case RTAX_IFA: 316 ift->ifa_addr = 317 (struct sockaddr *)(void *)data; 318 memcpy(data, p, len); 319 data += len; 320 break; 321 322 case RTAX_NETMASK: 323 ift->ifa_netmask = 324 (struct sockaddr *)(void *)data; 325 if (SA_LEN(sa) == 0) { 326 memset(data, 0, alen); 327 data += alen; 328 break; 329 } 330 memcpy(data, p, len); 331 data += len; 332 break; 333 334 case RTAX_BRD: 335 ift->ifa_broadaddr = 336 (struct sockaddr *)(void *)data; 337 memcpy(data, p, len); 338 data += len; 339 break; 340 } 341 p += len; 342 } 343 344 #ifdef HAVE_IFAM_DATA 345 /* ifam_data needs to be aligned */ 346 ift->ifa_data = data = (void *)ALIGN(data); 347 memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data)); 348 data += sizeof(ifam->ifam_data); 349 #endif /* HAVE_IFAM_DATA */ 350 351 ift = (ift->ifa_next = ift + 1); 352 break; 353 } 354 } 355 356 free(buf); 357 #else /* NET_RT_IFLIST */ 358 ifr = ifc.ifc_req; 359 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; 360 361 while (ifr < lifr) { 362 struct sockaddr *sa; 363 364 ift->ifa_name = names; 365 names[sizeof(ifr->ifr_name)] = 0; 366 strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name)); 367 while (*names++) 368 ; 369 370 ift->ifa_addr = (struct sockaddr *)data; 371 sa = &ifr->ifr_addr; 372 memcpy(data, sa, SA_LEN(sa)); 373 data += SA_RLEN(sa); 374 375 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); 376 ift = (ift->ifa_next = ift + 1); 377 } 378 #endif /* NET_RT_IFLIST */ 379 if (--ift >= ifa) { 380 ift->ifa_next = NULL; 381 *pif = ifa; 382 } else { 383 *pif = NULL; 384 free(ifa); 385 } 386 return (0); 387 } 388 389 void 390 freeifaddrs(struct ifaddrs *ifp) 391 { 392 393 free(ifp); 394 } 395