/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IPIF_SEPARATOR_CHAR ":" /* * Given an interface name, this function retrives the associated * index value. Returns index value if successful, zero otherwise. * The length of the supplied interface name must be at most * IF_NAMESIZE-1 bytes */ uint32_t if_nametoindex(const char *ifname) { int s; struct lifreq lifr; int save_err; size_t size; /* Make sure the given name is not NULL */ if ((ifname == NULL)||(*ifname == '\0')) { errno = ENXIO; return (0); } /* * Fill up the interface name in the ioctl * request message. Make sure that the length of * the given interface name <= (IF_NAMESIZE-1) */ size = strlen(ifname); if (size > (IF_NAMESIZE - 1)) { errno = EINVAL; return (0); } strncpy(lifr.lifr_name, ifname, size +1); /* Check the v4 interfaces first */ s = socket(AF_INET, SOCK_DGRAM, 0); if (s >= 0) { if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) { (void) close(s); return (lifr.lifr_index); } (void) close(s); } /* Check the v6 interface list */ s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) return (0); if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) < 0) lifr.lifr_index = 0; save_err = errno; (void) close(s); errno = save_err; return (lifr.lifr_index); } /* * Given an index, this function returns the associated interface * name in the supplied buffer ifname. * Returns physical interface name if successful, NULL otherwise. * The interface name returned will be at most IF_NAMESIZE-1 bytes. */ char * if_indextoname(uint32_t ifindex, char *ifname) { int n; int s; char *buf; uint32_t index; struct lifnum lifn; struct lifconf lifc; struct lifreq *lifrp; int numifs; size_t bufsize; boolean_t found; uint_t flags; flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES | LIFC_UNDER_IPMP; /* A interface index of 0 is invalid */ if (ifindex == 0) { errno = ENXIO; return (NULL); } s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) { s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { return (NULL); } } /* Prepare to send a SIOCGLIFNUM request message */ lifn.lifn_family = AF_UNSPEC; lifn.lifn_flags = flags; if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) { int save_err = errno; (void) close(s); errno = save_err; return (NULL); } /* * NOTE: "+ 10" sleaze mitigates new IP interfaces showing up between * the SIOCGLIFNUM and the SIOCGLIFCONF. */ numifs = lifn.lifn_count + 10; /* * Provide enough buffer to obtain the interface * list from the kernel as response to a SIOCGLIFCONF * request */ bufsize = numifs * sizeof (struct lifreq); buf = malloc(bufsize); if (buf == NULL) { int save_err = errno; (void) close(s); errno = save_err; return (NULL); } lifc.lifc_family = AF_UNSPEC; lifc.lifc_flags = flags; lifc.lifc_len = bufsize; lifc.lifc_buf = buf; if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { int save_err = errno; (void) close(s); errno = save_err; free(buf); return (NULL); } lifrp = lifc.lifc_req; found = B_FALSE; for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) { /* * Obtain the index value of each interface, and * match to see if the retrived index value matches * the given one. If so we return the corresponding * device name of that interface. */ size_t size; index = if_nametoindex(lifrp->lifr_name); if (index == 0) /* Oops the interface just disappeared */ continue; if (index == ifindex) { size = strcspn(lifrp->lifr_name, (char *)IPIF_SEPARATOR_CHAR); lifrp->lifr_name[size] = '\0'; found = B_TRUE; (void) strncpy(ifname, lifrp->lifr_name, size + 1); break; } } (void) close(s); free(buf); if (!found) { errno = ENXIO; return (NULL); } return (ifname); } /* * This function returns all the interface names and indexes */ struct if_nameindex * if_nameindex(void) { int n; int s; boolean_t found; char *buf; struct lifnum lifn; struct lifconf lifc; struct lifreq *lifrp; int numifs; int index; int i; int physinterf_num; size_t bufsize; struct if_nameindex *interface_list; struct if_nameindex *interface_entry; s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) { s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) return (NULL); } lifn.lifn_family = AF_UNSPEC; lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) return (NULL); numifs = lifn.lifn_count; bufsize = numifs * sizeof (struct lifreq); buf = malloc(bufsize); if (buf == NULL) { int save_err = errno; (void) close(s); errno = save_err; return (NULL); } lifc.lifc_family = AF_UNSPEC; lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES; lifc.lifc_len = bufsize; lifc.lifc_buf = buf; if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { int save_err = errno; (void) close(s); errno = save_err; free(buf); return (NULL); } lifrp = lifc.lifc_req; (void) close(s); /* Allocate the array of if_nameindex structure */ interface_list = malloc((numifs + 1) * sizeof (struct if_nameindex)); if (!interface_list) { int save_err = errno; free(buf); errno = save_err; return (NULL); } /* * Make sure that terminator structure automatically * happens to be all zeroes. */ bzero(interface_list, ((numifs + 1) * sizeof (struct if_nameindex))); interface_entry = interface_list; physinterf_num = 0; for (n = numifs; n > 0; n--, lifrp++) { size_t size; size = strcspn(lifrp->lifr_name, (char *)IPIF_SEPARATOR_CHAR); found = B_FALSE; /* * Search the current array to see if this interface * already exists. Only compare the physical name. */ for (i = 0; i < physinterf_num; i++) { if (strncmp(interface_entry[i].if_name, lifrp->lifr_name, size) == 0) { found = B_TRUE; break; } } /* New one. Allocate an array element and fill it */ if (!found) { /* * Obtain the index value for the interface */ interface_entry[physinterf_num].if_index = if_nametoindex(lifrp->lifr_name); if (interface_entry[physinterf_num].if_index == 0) { /* The interface went away. Skip this entry. */ continue; } /* * Truncate the name to ensure that it represents * a physical interface. */ lifrp->lifr_name[size] = '\0'; if ((interface_entry[physinterf_num].if_name = strdup(lifrp->lifr_name)) == NULL) { int save_err; if_freenameindex(interface_list); save_err = errno; free(buf); errno = save_err; return (NULL); } physinterf_num++; } } /* Create the last one of the array */ interface_entry[physinterf_num].if_name = NULL; interface_entry[physinterf_num].if_index = 0; /* Free up the excess array space */ free(buf); interface_list = realloc(interface_list, ((physinterf_num + 1) * sizeof (struct if_nameindex))); return (interface_list); } /* * This function frees the the array that is created while * the if_nameindex function. */ void if_freenameindex(struct if_nameindex *ptr) { struct if_nameindex *p; if (ptr == NULL) return; /* First free the if_name member in each array element */ for (p = ptr; p->if_name != NULL; p++) free(p->if_name); /* Now free up the array space */ free(ptr); }