getifaddrs.c revision 1.3
1/* $NetBSD: getifaddrs.c,v 1.3 2000/04/24 09:27:31 itojun Exp $ */ 2 3/* 4 * Copyright (c) 1995, 1999 5 * Berkeley Software Design, Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp 26 */ 27#include "namespace.h" 28#include <sys/types.h> 29#include <sys/ioctl.h> 30#include <sys/socket.h> 31#include <net/if.h> 32#ifdef NET_RT_IFLIST 33#include <sys/param.h> 34#include <net/route.h> 35#include <sys/sysctl.h> 36#include <net/if_dl.h> 37#endif 38 39#include <errno.h> 40#include <ifaddrs.h> 41#include <stdlib.h> 42#include <string.h> 43 44#ifdef __weak_alias 45__weak_alias(getifaddrs,_getifaddrs) 46__weak_alias(freeifaddrs,_freeifaddrs) 47#endif 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#define HAVE_IFAM_DATA 77#endif 78 79int 80getifaddrs(struct ifaddrs **pif) 81{ 82 int icnt = 1; 83 int dcnt = 0; 84 int ncnt = 0; 85#ifdef NET_RT_IFLIST 86 int mib[6]; 87 size_t needed; 88 char *buf; 89 char *next; 90 struct ifaddrs *cif = 0; 91 char *p, *p0; 92 struct rt_msghdr *rtm; 93 struct if_msghdr *ifm; 94 struct ifa_msghdr *ifam; 95 struct sockaddr_dl *dl; 96 struct sockaddr *sa; 97 struct ifaddrs *ifa, *ift; 98 u_short index = 0; 99#else /* NET_RT_IFLIST */ 100 char buf[1024]; 101 int m, sock; 102 struct ifconf ifc; 103 struct ifreq *ifr; 104 struct ifreq *lifr; 105#endif /* NET_RT_IFLIST */ 106 int i; 107 size_t len, alen; 108 char *data; 109 char *names; 110 111#ifdef NET_RT_IFLIST 112 mib[0] = CTL_NET; 113 mib[1] = PF_ROUTE; 114 mib[2] = 0; /* protocol */ 115 mib[3] = 0; /* wildcard address family */ 116 mib[4] = NET_RT_IFLIST; 117 mib[5] = 0; /* no flags */ 118 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 119 return (-1); 120 if ((buf = malloc(needed)) == NULL) 121 return (-1); 122 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 123 free(buf); 124 return (-1); 125 } 126 127 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 128 rtm = (struct rt_msghdr *)next; 129 if (rtm->rtm_version != RTM_VERSION) 130 continue; 131 switch (rtm->rtm_type) { 132 case RTM_IFINFO: 133 ifm = (struct if_msghdr *)rtm; 134 if (ifm->ifm_addrs & RTA_IFP) { 135 index = ifm->ifm_index; 136 ++icnt; 137 dl = (struct sockaddr_dl *)(ifm + 1); 138 dcnt += SA_RLEN((struct sockaddr *)dl) + 139 ALIGNBYTES; 140#ifdef HAVE_IFM_DATA 141 dcnt += sizeof(ifm->ifm_data); 142#endif /* HAVE_IFM_DATA */ 143 ncnt += dl->sdl_nlen + 1; 144 } else 145 index = 0; 146 break; 147 148 case RTM_NEWADDR: 149 ifam = (struct ifa_msghdr *)rtm; 150 if (index && ifam->ifam_index != index) 151 abort(); /* this cannot happen */ 152 153#define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD) 154 if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 155 break; 156 p = (char *)(ifam + 1); 157 ++icnt; 158#ifdef HAVE_IFAM_DATA 159 dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES; 160#endif /* HAVE_IFAM_DATA */ 161 /* Scan to look for length of address */ 162 alen = 0; 163 for (p0 = p, i = 0; i < RTAX_MAX; i++) { 164 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 165 == 0) 166 continue; 167 sa = (struct sockaddr *)p; 168 len = SA_RLEN(sa); 169 if (i == RTAX_IFA) { 170 alen = len; 171 break; 172 } 173 p += len; 174 } 175 for (p = p0, i = 0; i < RTAX_MAX; i++) { 176 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 177 == 0) 178 continue; 179 sa = (struct sockaddr *)p; 180 len = SA_RLEN(sa); 181 if (i == RTAX_NETMASK && SA_LEN(sa) == 0) 182 dcnt += alen; 183 else 184 dcnt += len; 185 p += len; 186 } 187 break; 188 } 189 } 190#else /* NET_RT_IFLIST */ 191 ifc.ifc_buf = buf; 192 ifc.ifc_len = sizeof(buf); 193 194 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 195 return (-1); 196 i = ioctl(sock, SIOCGIFCONF, (char *)&ifc); 197 close(sock); 198 if (i < 0) 199 return (-1); 200 201 ifr = ifc.ifc_req; 202 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; 203 204 while (ifr < lifr) { 205 struct sockaddr *sa; 206 207 sa = &ifr->ifr_addr; 208 ++icnt; 209 dcnt += SA_RLEN(sa); 210 ncnt += sizeof(ifr->ifr_name) + 1; 211 212 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); 213 } 214#endif /* NET_RT_IFLIST */ 215 216 if (icnt + dcnt + ncnt == 1) { 217 *pif = NULL; 218 free(buf); 219 return (0); 220 } 221 data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt); 222 if (data == NULL) { 223 free(buf); 224 return(-1); 225 } 226 227 ifa = (struct ifaddrs *)data; 228 data += sizeof(struct ifaddrs) * icnt; 229 names = data + dcnt; 230 231 memset(ifa, 0, sizeof(struct ifaddrs) * icnt); 232 ift = ifa; 233 234#ifdef NET_RT_IFLIST 235 index = 0; 236 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 237 rtm = (struct rt_msghdr *)next; 238 if (rtm->rtm_version != RTM_VERSION) 239 continue; 240 switch (rtm->rtm_type) { 241 case RTM_IFINFO: 242 ifm = (struct if_msghdr *)rtm; 243 if (ifm->ifm_addrs & RTA_IFP) { 244 index = ifm->ifm_index; 245 dl = (struct sockaddr_dl *)(ifm + 1); 246 247 cif = ift; 248 ift->ifa_name = names; 249 ift->ifa_flags = (int)ifm->ifm_flags; 250 memcpy(names, dl->sdl_data, dl->sdl_nlen); 251 names[dl->sdl_nlen] = 0; 252 names += dl->sdl_nlen + 1; 253 254 ift->ifa_addr = (struct sockaddr *)data; 255 memcpy(data, dl, SA_LEN((struct sockaddr *)dl)); 256 data += SA_RLEN((struct sockaddr *)dl); 257 258#ifdef HAVE_IFM_DATA 259 /* ifm_data needs to be aligned */ 260 ift->ifa_data = data = (void *)ALIGN(data); 261 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data)); 262 data += sizeof(ifm->ifm_data); 263#else /* HAVE_IFM_DATA */ 264 ift->ifa_data = NULL; 265#endif /* HAVE_IFM_DATA */ 266 267 ift = (ift->ifa_next = ift + 1); 268 } else 269 index = 0; 270 break; 271 272 case RTM_NEWADDR: 273 ifam = (struct ifa_msghdr *)rtm; 274 if (index && ifam->ifam_index != index) 275 abort(); /* this cannot happen */ 276 277 if (index == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 278 break; 279 ift->ifa_name = cif->ifa_name; 280 ift->ifa_flags = cif->ifa_flags; 281 ift->ifa_data = NULL; 282 p = (char *)(ifam + 1); 283 /* Scan to look for length of address */ 284 alen = 0; 285 for (p0 = p, i = 0; i < RTAX_MAX; i++) { 286 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 287 == 0) 288 continue; 289 sa = (struct sockaddr *)p; 290 len = SA_RLEN(sa); 291 if (i == RTAX_IFA) { 292 alen = len; 293 break; 294 } 295 p += len; 296 } 297 for (p = p0, i = 0; i < RTAX_MAX; i++) { 298 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 299 == 0) 300 continue; 301 sa = (struct sockaddr *)p; 302 len = SA_RLEN(sa); 303 switch (i) { 304 case RTAX_IFA: 305 ift->ifa_addr = (struct sockaddr *)data; 306 memcpy(data, p, len); 307 data += len; 308 break; 309 310 case RTAX_NETMASK: 311 ift->ifa_netmask = 312 (struct sockaddr *)data; 313 if (SA_LEN(sa) == 0) { 314 memset(data, 0, alen); 315 data += alen; 316 break; 317 } 318 memcpy(data, p, len); 319 data += len; 320 break; 321 322 case RTAX_BRD: 323 ift->ifa_broadaddr = 324 (struct sockaddr *)data; 325 memcpy(data, p, len); 326 data += len; 327 break; 328 } 329 p += len; 330 } 331 332#ifdef HAVE_IFAM_DATA 333 /* ifam_data needs to be aligned */ 334 ift->ifa_data = data = (void *)ALIGN(data); 335 memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data)); 336 data += sizeof(ifam->ifam_data); 337#endif /* HAVE_IFAM_DATA */ 338 339 ift = (ift->ifa_next = ift + 1); 340 break; 341 } 342 } 343 344 free(buf); 345#else /* NET_RT_IFLIST */ 346 ifr = ifc.ifc_req; 347 lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; 348 349 while (ifr < lifr) { 350 struct sockaddr *sa; 351 352 ift->ifa_name = names; 353 names[sizeof(ifr->ifr_name)] = 0; 354 strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name)); 355 while (*names++) 356 ; 357 358 ift->ifa_addr = (struct sockaddr *)data; 359 sa = &ifr->ifr_addr; 360 memcpy(data, sa, SA_LEN(sa)); 361 data += SA_RLEN(sa); 362 363 ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); 364 ift = (ift->ifa_next = ift + 1); 365 } 366#endif /* NET_RT_IFLIST */ 367 if (--ift >= ifa) { 368 ift->ifa_next = NULL; 369 *pif = ifa; 370 } else { 371 *pif = NULL; 372 free(ifa); 373 } 374 return (0); 375} 376 377void 378freeifaddrs(struct ifaddrs *ifp) 379{ 380 free(ifp); 381} 382