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 * Copyright 2017 RackTop Systems. 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 /* 107 * Initialize ifap to NULL so we can safely call freeifaddrs 108 * on it in case of error. 109 */ 110 if (ifap == NULL) 111 return (EINVAL); 112 *ifap = NULL; 113 114 if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 115 return (-1); 116 if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 117 err = errno; 118 close(sock4); 119 errno = err; 120 return (-1); 121 } 122 123 retry: 124 /* Get all interfaces from SIOCGLIFCONF */ 125 ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED)); 126 if (ret != 0) 127 goto fail; 128 129 /* 130 * Loop through the interfaces obtained from SIOCGLIFCOMF 131 * and retrieve the addresses, netmask and flags. 132 */ 133 prev = NULL; 134 lifrp = buf; 135 for (n = 0; n < numifs; n++, lifrp++) { 136 137 /* Prepare for the ioctl call */ 138 (void) strncpy(lifrl.lifr_name, lifrp->lifr_name, 139 sizeof (lifrl.lifr_name)); 140 lifr_af = lifrp->lifr_addr.ss_family; 141 if (af != AF_UNSPEC && lifr_af != af) 142 continue; 143 144 s = (lifr_af == AF_INET ? sock4 : sock6); 145 146 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) 147 goto fail; 148 if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP)) 149 continue; 150 151 /* 152 * Allocate the current list node. Each node contains data 153 * for one ifaddrs structure. 154 */ 155 curr = calloc(1, sizeof (struct ifaddrs)); 156 if (curr == NULL) 157 goto fail; 158 159 if (prev != NULL) { 160 prev->ifa_next = curr; 161 } else { 162 /* First node in the linked list */ 163 *ifap = curr; 164 } 165 prev = curr; 166 167 curr->ifa_flags = lifrl.lifr_flags; 168 if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL) 169 goto fail; 170 171 curr->ifa_addr = malloc(sizeof (struct sockaddr_storage)); 172 if (curr->ifa_addr == NULL) 173 goto fail; 174 (void) memcpy(curr->ifa_addr, &lifrp->lifr_addr, 175 sizeof (struct sockaddr_storage)); 176 177 /* Get the netmask */ 178 if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0) 179 goto fail; 180 curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage)); 181 if (curr->ifa_netmask == NULL) 182 goto fail; 183 (void) memcpy(curr->ifa_netmask, &lifrl.lifr_addr, 184 sizeof (struct sockaddr_storage)); 185 186 /* Get the destination for a pt-pt interface */ 187 if (curr->ifa_flags & IFF_POINTOPOINT) { 188 if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0) 189 goto fail; 190 curr->ifa_dstaddr = malloc( 191 sizeof (struct sockaddr_storage)); 192 if (curr->ifa_dstaddr == NULL) 193 goto fail; 194 (void) memcpy(curr->ifa_dstaddr, &lifrl.lifr_addr, 195 sizeof (struct sockaddr_storage)); 196 } else if (curr->ifa_flags & IFF_BROADCAST) { 197 if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0) 198 goto fail; 199 curr->ifa_broadaddr = malloc( 200 sizeof (struct sockaddr_storage)); 201 if (curr->ifa_broadaddr == NULL) 202 goto fail; 203 (void) memcpy(curr->ifa_broadaddr, &lifrl.lifr_addr, 204 sizeof (struct sockaddr_storage)); 205 } 206 207 } 208 free(buf); 209 close(sock4); 210 close(sock6); 211 return (0); 212 fail: 213 err = errno; 214 free(buf); 215 freeifaddrs(*ifap); 216 *ifap = NULL; 217 if (err == ENXIO) 218 goto retry; 219 close(sock4); 220 close(sock6); 221 errno = err; 222 return (-1); 223 } 224 225 /* 226 * Do a SIOCGLIFCONF and store all the interfaces in `buf'. 227 */ 228 int 229 getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs, 230 int64_t lifc_flags) 231 { 232 struct lifnum lifn; 233 struct lifconf lifc; 234 size_t bufsize; 235 char *tmp; 236 caddr_t *buf = (caddr_t *)lifr; 237 238 lifn.lifn_family = af; 239 lifn.lifn_flags = lifc_flags; 240 241 *buf = NULL; 242 retry: 243 if (ioctl(s, SIOCGLIFNUM, &lifn) < 0) 244 goto fail; 245 246 /* 247 * When calculating the buffer size needed, add a small number 248 * of interfaces to those we counted. We do this to capture 249 * the interface status of potential interfaces which may have 250 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF. 251 */ 252 bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq); 253 254 if ((tmp = realloc(*buf, bufsize)) == NULL) 255 goto fail; 256 257 *buf = tmp; 258 lifc.lifc_family = af; 259 lifc.lifc_flags = lifc_flags; 260 lifc.lifc_len = bufsize; 261 lifc.lifc_buf = *buf; 262 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) 263 goto fail; 264 265 *numifs = lifc.lifc_len / sizeof (struct lifreq); 266 if (*numifs >= (lifn.lifn_count + 4)) { 267 /* 268 * If every entry was filled, there are probably 269 * more interfaces than (lifn.lifn_count + 4). 270 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to 271 * get all the interfaces. 272 */ 273 goto retry; 274 } 275 return (0); 276 fail: 277 free(*buf); 278 *buf = NULL; 279 return (-1); 280 }