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) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <stdio.h> 27 #include <ctype.h> 28 #include <string.h> 29 #include <strings.h> 30 #include <stdlib.h> 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <inet/common.h> 34 #include <net/if.h> 35 #include <netinet/in.h> 36 #include <sys/sockio.h> 37 #include <sys/ioctl.h> 38 #include <unistd.h> 39 #include <errno.h> 40 41 #define IPIF_SEPARATOR_CHAR ":" 42 43 /* 44 * Given an interface name, this function retrives the associated 45 * index value. Returns index value if successful, zero otherwise. 46 * The length of the supplied interface name must be at most 47 * IF_NAMESIZE-1 bytes 48 */ 49 uint32_t 50 if_nametoindex(const char *ifname) 51 { 52 int s; 53 struct lifreq lifr; 54 int save_err; 55 size_t size; 56 57 58 /* Make sure the given name is not NULL */ 59 if ((ifname == NULL)||(*ifname == '\0')) { 60 errno = ENXIO; 61 return (0); 62 } 63 64 /* 65 * Fill up the interface name in the ioctl 66 * request message. Make sure that the length of 67 * the given interface name <= (IF_NAMESIZE-1) 68 */ 69 size = strlen(ifname); 70 if (size > (IF_NAMESIZE - 1)) { 71 errno = EINVAL; 72 return (0); 73 } 74 75 strncpy(lifr.lifr_name, ifname, size +1); 76 77 /* Check the v4 interfaces first */ 78 s = socket(AF_INET, SOCK_DGRAM, 0); 79 if (s >= 0) { 80 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) { 81 (void) close(s); 82 return (lifr.lifr_index); 83 } 84 (void) close(s); 85 } 86 87 /* Check the v6 interface list */ 88 s = socket(AF_INET6, SOCK_DGRAM, 0); 89 if (s < 0) 90 return (0); 91 92 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) < 0) 93 lifr.lifr_index = 0; 94 95 save_err = errno; 96 (void) close(s); 97 errno = save_err; 98 return (lifr.lifr_index); 99 } 100 101 /* 102 * Given an index, this function returns the associated interface 103 * name in the supplied buffer ifname. 104 * Returns physical interface name if successful, NULL otherwise. 105 * The interface name returned will be at most IF_NAMESIZE-1 bytes. 106 */ 107 char * 108 if_indextoname(uint32_t ifindex, char *ifname) 109 { 110 int n; 111 int s; 112 char *buf; 113 uint32_t index; 114 struct lifnum lifn; 115 struct lifconf lifc; 116 struct lifreq *lifrp; 117 int numifs; 118 size_t bufsize; 119 boolean_t found; 120 uint_t flags; 121 122 flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES | LIFC_UNDER_IPMP; 123 124 /* A interface index of 0 is invalid */ 125 if (ifindex == 0) { 126 errno = ENXIO; 127 return (NULL); 128 } 129 130 s = socket(AF_INET6, SOCK_DGRAM, 0); 131 if (s < 0) { 132 s = socket(AF_INET, SOCK_DGRAM, 0); 133 if (s < 0) { 134 return (NULL); 135 } 136 } 137 138 /* Prepare to send a SIOCGLIFNUM request message */ 139 lifn.lifn_family = AF_UNSPEC; 140 lifn.lifn_flags = flags; 141 if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) { 142 int save_err = errno; 143 (void) close(s); 144 errno = save_err; 145 return (NULL); 146 } 147 148 /* 149 * NOTE: "+ 10" sleaze mitigates new IP interfaces showing up between 150 * the SIOCGLIFNUM and the SIOCGLIFCONF. 151 */ 152 numifs = lifn.lifn_count + 10; 153 154 /* 155 * Provide enough buffer to obtain the interface 156 * list from the kernel as response to a SIOCGLIFCONF 157 * request 158 */ 159 160 bufsize = numifs * sizeof (struct lifreq); 161 buf = malloc(bufsize); 162 if (buf == NULL) { 163 int save_err = errno; 164 (void) close(s); 165 errno = save_err; 166 return (NULL); 167 } 168 lifc.lifc_family = AF_UNSPEC; 169 lifc.lifc_flags = flags; 170 lifc.lifc_len = bufsize; 171 lifc.lifc_buf = buf; 172 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { 173 int save_err = errno; 174 (void) close(s); 175 errno = save_err; 176 free(buf); 177 return (NULL); 178 } 179 180 lifrp = lifc.lifc_req; 181 found = B_FALSE; 182 for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) { 183 /* 184 * Obtain the index value of each interface, and 185 * match to see if the retrived index value matches 186 * the given one. If so we return the corresponding 187 * device name of that interface. 188 */ 189 size_t size; 190 191 index = if_nametoindex(lifrp->lifr_name); 192 if (index == 0) 193 /* Oops the interface just disappeared */ 194 continue; 195 if (index == ifindex) { 196 size = strcspn(lifrp->lifr_name, 197 (char *)IPIF_SEPARATOR_CHAR); 198 lifrp->lifr_name[size] = '\0'; 199 found = B_TRUE; 200 (void) strncpy(ifname, lifrp->lifr_name, size + 1); 201 break; 202 } 203 } 204 (void) close(s); 205 free(buf); 206 if (!found) { 207 errno = ENXIO; 208 return (NULL); 209 } 210 return (ifname); 211 } 212 213 /* 214 * This function returns all the interface names and indexes 215 */ 216 struct if_nameindex * 217 if_nameindex(void) 218 { 219 int n; 220 int s; 221 boolean_t found; 222 char *buf; 223 struct lifnum lifn; 224 struct lifconf lifc; 225 struct lifreq *lifrp; 226 int numifs; 227 int index; 228 int i; 229 int physinterf_num; 230 size_t bufsize; 231 struct if_nameindex *interface_list; 232 struct if_nameindex *interface_entry; 233 234 s = socket(AF_INET6, SOCK_DGRAM, 0); 235 if (s < 0) { 236 s = socket(AF_INET, SOCK_DGRAM, 0); 237 if (s < 0) 238 return (NULL); 239 } 240 241 lifn.lifn_family = AF_UNSPEC; 242 lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 243 if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) 244 return (NULL); 245 numifs = lifn.lifn_count; 246 247 bufsize = numifs * sizeof (struct lifreq); 248 buf = malloc(bufsize); 249 if (buf == NULL) { 250 int save_err = errno; 251 (void) close(s); 252 errno = save_err; 253 return (NULL); 254 } 255 lifc.lifc_family = AF_UNSPEC; 256 lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 257 lifc.lifc_len = bufsize; 258 lifc.lifc_buf = buf; 259 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { 260 int save_err = errno; 261 (void) close(s); 262 errno = save_err; 263 free(buf); 264 return (NULL); 265 } 266 267 lifrp = lifc.lifc_req; 268 (void) close(s); 269 270 /* Allocate the array of if_nameindex structure */ 271 interface_list = malloc((numifs + 1) * sizeof (struct if_nameindex)); 272 if (!interface_list) { 273 int save_err = errno; 274 free(buf); 275 errno = save_err; 276 return (NULL); 277 } 278 /* 279 * Make sure that terminator structure automatically 280 * happens to be all zeroes. 281 */ 282 bzero(interface_list, ((numifs + 1) * sizeof (struct if_nameindex))); 283 interface_entry = interface_list; 284 physinterf_num = 0; 285 for (n = numifs; n > 0; n--, lifrp++) { 286 size_t size; 287 288 size = strcspn(lifrp->lifr_name, (char *)IPIF_SEPARATOR_CHAR); 289 found = B_FALSE; 290 /* 291 * Search the current array to see if this interface 292 * already exists. Only compare the physical name. 293 */ 294 for (i = 0; i < physinterf_num; i++) { 295 if (strncmp(interface_entry[i].if_name, 296 lifrp->lifr_name, size) == 0) { 297 found = B_TRUE; 298 break; 299 } 300 } 301 302 /* New one. Allocate an array element and fill it */ 303 if (!found) { 304 /* 305 * Obtain the index value for the interface 306 */ 307 interface_entry[physinterf_num].if_index = 308 if_nametoindex(lifrp->lifr_name); 309 310 if (interface_entry[physinterf_num].if_index == 0) { 311 /* The interface went away. Skip this entry. */ 312 continue; 313 } 314 315 /* 316 * Truncate the name to ensure that it represents 317 * a physical interface. 318 */ 319 lifrp->lifr_name[size] = '\0'; 320 if ((interface_entry[physinterf_num].if_name = 321 strdup(lifrp->lifr_name)) == NULL) { 322 int save_err; 323 324 if_freenameindex(interface_list); 325 save_err = errno; 326 free(buf); 327 errno = save_err; 328 return (NULL); 329 } 330 331 physinterf_num++; 332 } 333 } 334 335 /* Create the last one of the array */ 336 interface_entry[physinterf_num].if_name = NULL; 337 interface_entry[physinterf_num].if_index = 0; 338 339 /* Free up the excess array space */ 340 free(buf); 341 interface_list = realloc(interface_list, ((physinterf_num + 1) * 342 sizeof (struct if_nameindex))); 343 344 return (interface_list); 345 } 346 347 /* 348 * This function frees the the array that is created while 349 * the if_nameindex function. 350 */ 351 void 352 if_freenameindex(struct if_nameindex *ptr) 353 { 354 struct if_nameindex *p; 355 356 if (ptr == NULL) 357 return; 358 359 /* First free the if_name member in each array element */ 360 for (p = ptr; p->if_name != NULL; p++) 361 free(p->if_name); 362 363 /* Now free up the array space */ 364 free(ptr); 365 } 366