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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 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 have 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, 201 size + 1); 202 break; 203 } 204 } 205 (void) close(s); 206 free(buf); 207 if (!found) { 208 errno = ENXIO; 209 return (NULL); 210 } 211 return (ifname); 212 } 213 214 /* 215 * This function returns all the interface names and indexes 216 */ 217 struct if_nameindex * 218 if_nameindex(void) 219 { 220 int n; 221 int s; 222 boolean_t found; 223 char *buf; 224 struct lifnum lifn; 225 struct lifconf lifc; 226 struct lifreq *lifrp; 227 int numifs; 228 int index; 229 int i; 230 int physinterf_num; 231 size_t bufsize; 232 struct if_nameindex *interface_list; 233 struct if_nameindex *interface_entry; 234 235 s = socket(AF_INET6, SOCK_DGRAM, 0); 236 if (s < 0) { 237 s = socket(AF_INET, SOCK_DGRAM, 0); 238 if (s < 0) 239 return (NULL); 240 } 241 242 lifn.lifn_family = AF_UNSPEC; 243 lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 244 if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) 245 return (NULL); 246 numifs = lifn.lifn_count; 247 248 bufsize = numifs * sizeof (struct lifreq); 249 buf = malloc(bufsize); 250 if (buf == NULL) { 251 int save_err = errno; 252 (void) close(s); 253 errno = save_err; 254 return (NULL); 255 } 256 lifc.lifc_family = AF_UNSPEC; 257 lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; 258 lifc.lifc_len = bufsize; 259 lifc.lifc_buf = buf; 260 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { 261 int save_err = errno; 262 (void) close(s); 263 errno = save_err; 264 free(buf); 265 return (NULL); 266 } 267 268 lifrp = lifc.lifc_req; 269 (void) close(s); 270 271 /* Allocate the array of if_nameindex structure */ 272 interface_list = malloc((numifs + 1) * sizeof (struct if_nameindex)); 273 if (!interface_list) { 274 int save_err = errno; 275 free(buf); 276 errno = save_err; 277 return (NULL); 278 } 279 /* 280 * Make sure that terminator structure automatically 281 * happens to be all zeroes. 282 */ 283 bzero(interface_list, ((numifs + 1) * sizeof (struct if_nameindex))); 284 interface_entry = interface_list; 285 physinterf_num = 0; 286 for (n = numifs; n > 0; n--, lifrp++) { 287 size_t size; 288 289 size = strcspn(lifrp->lifr_name, (char *)IPIF_SEPARATOR_CHAR); 290 lifrp->lifr_name[size] = '\0'; 291 found = B_FALSE; 292 /* 293 * Search the current array to see if this interface 294 * already exists 295 */ 296 297 for (i = 0; i < physinterf_num; i++) { 298 if (strcmp(interface_entry[i].if_name, 299 lifrp->lifr_name) == 0) { 300 found = B_TRUE; 301 break; 302 } 303 } 304 305 allocate_new: 306 /* New one. Allocate an array element and fill it */ 307 if (!found) { 308 if ((interface_entry[physinterf_num].if_name = 309 strdup(lifrp->lifr_name)) == NULL) { 310 int save_err; 311 312 if_freenameindex(interface_list); 313 save_err = errno; 314 free(buf); 315 errno = save_err; 316 return (NULL); 317 } 318 319 /* 320 * Obtain the index value for the interface 321 */ 322 interface_entry[physinterf_num].if_index = 323 if_nametoindex(lifrp->lifr_name); 324 physinterf_num++; 325 } 326 } 327 328 /* Create the last one of the array */ 329 interface_entry[physinterf_num].if_name = NULL; 330 interface_entry[physinterf_num].if_index = 0; 331 332 /* Free up the excess array space */ 333 free(buf); 334 interface_list = realloc(interface_list, ((physinterf_num + 1) * 335 sizeof (struct if_nameindex))); 336 337 return (interface_list); 338 } 339 340 /* 341 * This function frees the the array that is created while 342 * the if_nameindex function. 343 */ 344 void 345 if_freenameindex(struct if_nameindex *ptr) 346 { 347 348 if (ptr == NULL) 349 return; 350 351 352 /* First free the if_name member in each array element */ 353 while (ptr->if_name != NULL) { 354 free(ptr->if_name); 355 ptr++; 356 } 357 358 /* Now free up the array space */ 359 free(ptr); 360 } 361