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