getifaddrs.c revision 1.12
1/* $NetBSD: getifaddrs.c,v 1.12 2009/04/27 20:10:49 dyoung 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 28#include <sys/cdefs.h> 29#if defined(LIBC_SCCS) && !defined(lint) 30__RCSID("$NetBSD: getifaddrs.c,v 1.12 2009/04/27 20:10:49 dyoung Exp $"); 31#endif /* LIBC_SCCS and not lint */ 32 33#include "namespace.h" 34#include <sys/types.h> 35#include <sys/ioctl.h> 36#include <sys/socket.h> 37#include <net/if.h> 38#include <sys/param.h> 39#include <net/route.h> 40#include <sys/sysctl.h> 41#include <net/if_dl.h> 42 43#include <assert.h> 44#include <errno.h> 45#include <ifaddrs.h> 46#include <stdlib.h> 47#include <string.h> 48 49#ifdef __weak_alias 50__weak_alias(getifaddrs,_getifaddrs) 51__weak_alias(freeifaddrs,_freeifaddrs) 52#endif 53 54#define SALIGN (sizeof(long) - 1) 55#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1)) 56 57int 58getifaddrs(struct ifaddrs **pif) 59{ 60 int icnt = 1; 61 int dcnt = 0; 62 int ncnt = 0; 63 int mib[6]; 64 size_t needed; 65 char *buf; 66 char *next; 67 struct ifaddrs cif; 68 char *p, *p0; 69 struct rt_msghdr *rtm; 70 struct if_msghdr *ifm; 71 struct ifa_msghdr *ifam; 72 struct sockaddr *sa; 73 struct ifaddrs *ifa, *ift; 74 u_short idx = 0; 75 int i; 76 size_t len, alen; 77 char *data; 78 char *names; 79 80 _DIAGASSERT(pif != NULL); 81 82 mib[0] = CTL_NET; 83 mib[1] = PF_ROUTE; 84 mib[2] = 0; /* protocol */ 85 mib[3] = 0; /* wildcard address family */ 86 mib[4] = NET_RT_IFLIST; 87 mib[5] = 0; /* no flags */ 88 if (sysctl(mib, __arraycount(mib), NULL, &needed, NULL, 0) < 0) 89 return (-1); 90 if ((buf = malloc(needed)) == NULL) 91 return (-1); 92 if (sysctl(mib, __arraycount(mib), buf, &needed, NULL, 0) < 0) { 93 free(buf); 94 return (-1); 95 } 96 97 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 98 rtm = (struct rt_msghdr *)(void *)next; 99 if (rtm->rtm_version != RTM_VERSION) 100 continue; 101 switch (rtm->rtm_type) { 102 case RTM_IFINFO: 103 ifm = (struct if_msghdr *)(void *)rtm; 104 if (ifm->ifm_addrs & RTA_IFP) { 105 const struct sockaddr_dl *dl; 106 107 idx = ifm->ifm_index; 108 ++icnt; 109 dl = (struct sockaddr_dl *)(void *)(ifm + 1); 110 dcnt += SA_RLEN((const struct sockaddr *)(const void *)dl) + 111 ALIGNBYTES; 112 dcnt += sizeof(ifm->ifm_data); 113 ncnt += dl->sdl_nlen + 1; 114 } else 115 idx = 0; 116 break; 117 118 case RTM_NEWADDR: 119 ifam = (struct ifa_msghdr *)(void *)rtm; 120 if (idx && ifam->ifam_index != idx) 121 abort(); /* this cannot happen */ 122 123#define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD) 124 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 125 break; 126 p = (char *)(void *)(ifam + 1); 127 ++icnt; 128 /* Scan to look for length of address */ 129 alen = 0; 130 for (p0 = p, i = 0; i < RTAX_MAX; i++) { 131 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 132 == 0) 133 continue; 134 sa = (struct sockaddr *)(void *)p; 135 len = SA_RLEN(sa); 136 if (i == RTAX_IFA) { 137 alen = len; 138 break; 139 } 140 p += len; 141 } 142 for (p = p0, i = 0; i < RTAX_MAX; i++) { 143 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 144 == 0) 145 continue; 146 sa = (struct sockaddr *)(void *)p; 147 len = SA_RLEN(sa); 148 if (i == RTAX_NETMASK && sa->sa_len == 0) 149 dcnt += alen; 150 else 151 dcnt += len; 152 p += len; 153 } 154 break; 155 } 156 } 157 158 if (icnt + dcnt + ncnt == 1) { 159 *pif = NULL; 160 free(buf); 161 return (0); 162 } 163 data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt); 164 if (data == NULL) { 165 free(buf); 166 return(-1); 167 } 168 169 ifa = (struct ifaddrs *)(void *)data; 170 data += sizeof(struct ifaddrs) * icnt; 171 names = data + dcnt; 172 173 memset(ifa, 0, sizeof(struct ifaddrs) * icnt); 174 ift = ifa; 175 176 idx = 0; 177 for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { 178 rtm = (struct rt_msghdr *)(void *)next; 179 if (rtm->rtm_version != RTM_VERSION) 180 continue; 181 switch (rtm->rtm_type) { 182 case RTM_IFINFO: 183 ifm = (struct if_msghdr *)(void *)rtm; 184 if (ifm->ifm_addrs & RTA_IFP) { 185 const struct sockaddr_dl *dl; 186 187 idx = ifm->ifm_index; 188 dl = (struct sockaddr_dl *)(void *)(ifm + 1); 189 190 memset(&cif, 0, sizeof(cif)); 191 192 cif.ifa_name = names; 193 cif.ifa_flags = (int)ifm->ifm_flags; 194 memcpy(names, dl->sdl_data, 195 (size_t)dl->sdl_nlen); 196 names[dl->sdl_nlen] = 0; 197 names += dl->sdl_nlen + 1; 198 199 cif.ifa_addr = (struct sockaddr *)(void *)data; 200 memcpy(data, dl, (size_t)dl->sdl_len); 201 data += SA_RLEN((const struct sockaddr *)(const void *)dl); 202 203 /* ifm_data needs to be aligned */ 204 cif.ifa_data = data = (void *)ALIGN(data); 205 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data)); 206 data += sizeof(ifm->ifm_data); 207 } else 208 idx = 0; 209 break; 210 211 case RTM_NEWADDR: 212 ifam = (struct ifa_msghdr *)(void *)rtm; 213 if (idx && ifam->ifam_index != idx) 214 abort(); /* this cannot happen */ 215 216 if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) 217 break; 218 ift->ifa_name = cif.ifa_name; 219 ift->ifa_flags = cif.ifa_flags; 220 ift->ifa_data = NULL; 221 p = (char *)(void *)(ifam + 1); 222 /* Scan to look for length of address */ 223 alen = 0; 224 for (p0 = p, i = 0; i < RTAX_MAX; i++) { 225 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 226 == 0) 227 continue; 228 sa = (struct sockaddr *)(void *)p; 229 len = SA_RLEN(sa); 230 if (i == RTAX_IFA) { 231 alen = len; 232 break; 233 } 234 p += len; 235 } 236 for (p = p0, i = 0; i < RTAX_MAX; i++) { 237 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) 238 == 0) 239 continue; 240 sa = (struct sockaddr *)(void *)p; 241 len = SA_RLEN(sa); 242 switch (i) { 243 case RTAX_IFA: 244 ift->ifa_addr = 245 (struct sockaddr *)(void *)data; 246 memcpy(data, p, len); 247 data += len; 248 if (ift->ifa_addr->sa_family == AF_LINK) 249 ift->ifa_data = cif.ifa_data; 250 break; 251 252 case RTAX_NETMASK: 253 ift->ifa_netmask = 254 (struct sockaddr *)(void *)data; 255 if (sa->sa_len == 0) { 256 memset(data, 0, alen); 257 data += alen; 258 break; 259 } 260 memcpy(data, p, len); 261 data += len; 262 break; 263 264 case RTAX_BRD: 265 ift->ifa_broadaddr = 266 (struct sockaddr *)(void *)data; 267 memcpy(data, p, len); 268 data += len; 269 break; 270 } 271 p += len; 272 } 273 274 275 ift = (ift->ifa_next = ift + 1); 276 break; 277 } 278 } 279 280 free(buf); 281 if (--ift >= ifa) { 282 ift->ifa_next = NULL; 283 *pif = ifa; 284 } else { 285 *pif = NULL; 286 free(ifa); 287 } 288 return (0); 289} 290 291void 292freeifaddrs(struct ifaddrs *ifp) 293{ 294 295 _DIAGASSERT(ifp != NULL); 296 297 free(ifp); 298} 299