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