1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <netdb.h> 28 #include <nss_dbdefs.h> 29 #include <netinet/in.h> 30 #include <sys/socket.h> 31 #include <string.h> 32 #include <stdio.h> 33 #include <sys/sockio.h> 34 #include <sys/types.h> 35 #include <stdlib.h> 36 #include <net/if.h> 37 #include <ifaddrs.h> 38 #include <libsocket_priv.h> 39 40 /* 41 * Create a linked list of `struct ifaddrs' structures, one for each 42 * address that is UP. If successful, store the list in *ifap and 43 * return 0. On errors, return -1 and set `errno'. 44 * 45 * The storage returned in *ifap is allocated dynamically and can 46 * only be properly freed by passing it to `freeifaddrs'. 47 */ 48 int 49 getifaddrs(struct ifaddrs **ifap) 50 { 51 int err; 52 char *cp; 53 struct ifaddrs *curr; 54 55 if (ifap == NULL) { 56 errno = EINVAL; 57 return (-1); 58 } 59 *ifap = NULL; 60 err = getallifaddrs(AF_UNSPEC, ifap, LIFC_ENABLED); 61 if (err == 0) { 62 for (curr = *ifap; curr != NULL; curr = curr->ifa_next) { 63 if ((cp = strchr(curr->ifa_name, ':')) != NULL) 64 *cp = '\0'; 65 } 66 } 67 return (err); 68 } 69 70 void 71 freeifaddrs(struct ifaddrs *ifa) 72 { 73 struct ifaddrs *curr; 74 75 while (ifa != NULL) { 76 curr = ifa; 77 ifa = ifa->ifa_next; 78 free(curr->ifa_name); 79 free(curr->ifa_addr); 80 free(curr->ifa_netmask); 81 free(curr->ifa_dstaddr); 82 free(curr); 83 } 84 } 85 86 /* 87 * Returns all addresses configured on the system. If flags contain 88 * LIFC_ENABLED, only the addresses that are UP are returned. 89 * Address list that is returned by this function must be freed 90 * using freeifaddrs(). 91 */ 92 int 93 getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags) 94 { 95 struct lifreq *buf = NULL; 96 struct lifreq *lifrp; 97 struct lifreq lifrl; 98 int ret; 99 int s, n, numifs; 100 struct ifaddrs *curr, *prev; 101 sa_family_t lifr_af; 102 int sock4; 103 int sock6; 104 int err; 105 106 if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 107 return (-1); 108 if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 109 err = errno; 110 close(sock4); 111 errno = err; 112 return (-1); 113 } 114 115 retry: 116 /* Get all interfaces from SIOCGLIFCONF */ 117 ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED)); 118 if (ret != 0) 119 goto fail; 120 121 /* 122 * Loop through the interfaces obtained from SIOCGLIFCOMF 123 * and retrieve the addresses, netmask and flags. 124 */ 125 prev = NULL; 126 lifrp = buf; 127 *ifap = NULL; 128 for (n = 0; n < numifs; n++, lifrp++) { 129 130 /* Prepare for the ioctl call */ 131 (void) strncpy(lifrl.lifr_name, lifrp->lifr_name, 132 sizeof (lifrl.lifr_name)); 133 lifr_af = lifrp->lifr_addr.ss_family; 134 if (af != AF_UNSPEC && lifr_af != af) 135 continue; 136 137 s = (lifr_af == AF_INET ? sock4 : sock6); 138 139 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) 140 goto fail; 141 if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP)) 142 continue; 143 144 /* 145 * Allocate the current list node. Each node contains data 146 * for one ifaddrs structure. 147 */ 148 curr = calloc(1, sizeof (struct ifaddrs)); 149 if (curr == NULL) 150 goto fail; 151 152 if (prev != NULL) { 153 prev->ifa_next = curr; 154 } else { 155 /* First node in the linked list */ 156 *ifap = curr; 157 } 158 prev = curr; 159 160 curr->ifa_flags = lifrl.lifr_flags; 161 if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL) 162 goto fail; 163 164 curr->ifa_addr = malloc(sizeof (struct sockaddr_storage)); 165 if (curr->ifa_addr == NULL) 166 goto fail; 167 *curr->ifa_addr = lifrp->lifr_addr; 168 169 /* Get the netmask */ 170 if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0) 171 goto fail; 172 curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage)); 173 if (curr->ifa_netmask == NULL) 174 goto fail; 175 *curr->ifa_netmask = lifrl.lifr_addr; 176 177 /* Get the destination for a pt-pt interface */ 178 if (curr->ifa_flags & IFF_POINTOPOINT) { 179 if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0) 180 goto fail; 181 curr->ifa_dstaddr = malloc( 182 sizeof (struct sockaddr_storage)); 183 if (curr->ifa_dstaddr == NULL) 184 goto fail; 185 *curr->ifa_dstaddr = lifrl.lifr_addr; 186 } else if (curr->ifa_flags & IFF_BROADCAST) { 187 if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0) 188 goto fail; 189 curr->ifa_broadaddr = malloc( 190 sizeof (struct sockaddr_storage)); 191 if (curr->ifa_broadaddr == NULL) 192 goto fail; 193 *curr->ifa_broadaddr = lifrl.lifr_addr; 194 } 195 196 } 197 free(buf); 198 close(sock4); 199 close(sock6); 200 return (0); 201 fail: 202 err = errno; 203 free(buf); 204 freeifaddrs(*ifap); 205 *ifap = NULL; 206 if (err == ENXIO) 207 goto retry; 208 close(sock4); 209 close(sock6); 210 errno = err; 211 return (-1); 212 } 213 214 /* 215 * Do a SIOCGLIFCONF and store all the interfaces in `buf'. 216 */ 217 int 218 getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs, 219 int64_t lifc_flags) 220 { 221 struct lifnum lifn; 222 struct lifconf lifc; 223 size_t bufsize; 224 char *tmp; 225 caddr_t *buf = (caddr_t *)lifr; 226 227 lifn.lifn_family = af; 228 lifn.lifn_flags = lifc_flags; 229 230 *buf = NULL; 231 retry: 232 if (ioctl(s, SIOCGLIFNUM, &lifn) < 0) 233 goto fail; 234 235 /* 236 * When calculating the buffer size needed, add a small number 237 * of interfaces to those we counted. We do this to capture 238 * the interface status of potential interfaces which may have 239 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF. 240 */ 241 bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq); 242 243 if ((tmp = realloc(*buf, bufsize)) == NULL) 244 goto fail; 245 246 *buf = tmp; 247 lifc.lifc_family = af; 248 lifc.lifc_flags = lifc_flags; 249 lifc.lifc_len = bufsize; 250 lifc.lifc_buf = *buf; 251 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) 252 goto fail; 253 254 *numifs = lifc.lifc_len / sizeof (struct lifreq); 255 if (*numifs >= (lifn.lifn_count + 4)) { 256 /* 257 * If every entry was filled, there are probably 258 * more interfaces than (lifn.lifn_count + 4). 259 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to 260 * get all the interfaces. 261 */ 262 goto retry; 263 } 264 return (0); 265 fail: 266 free(*buf); 267 *buf = NULL; 268 return (-1); 269 } 270