Home | History | Annotate | Download | only in inet
      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 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 
    355 	if (ptr == NULL)
    356 		return;
    357 
    358 
    359 	/* First free the if_name member in each array element */
    360 	while (ptr->if_name != NULL) {
    361 		free(ptr->if_name);
    362 		ptr++;
    363 	}
    364 
    365 	/* Now free up the array space */
    366 	free(ptr);
    367 }
    368