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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 */ 24 25 #include <errno.h> 26 #include <libinetutil.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <sys/socket.h> 32 #include <sys/sockio.h> 33 34 /* 35 * Create a list of the addresses on physical interface `ifname' with at least 36 * one of the flags in `set' set and all of the flags in `clear' clear. 37 * Return the number of items in the list, or -1 on failure. 38 */ 39 int 40 ifaddrlistx(const char *ifname, uint64_t set, uint64_t clear, 41 ifaddrlistx_t **ifaddrsp) 42 { 43 struct lifconf lifc; 44 struct lifnum lifn; 45 struct lifreq *lifrp; 46 ifaddrlistx_t *ifaddrp, *ifaddrs = NULL; 47 int i, nlifr, naddr = 0; 48 char *cp; 49 uint_t flags; 50 int s4, s6 = -1; 51 boolean_t isv6; 52 int save_errno; 53 struct sockaddr_storage addr; 54 55 (void) memset(&lifc, 0, sizeof (lifc)); 56 flags = LIFC_NOXMIT | LIFC_ALLZONES | LIFC_TEMPORARY | LIFC_UNDER_IPMP; 57 58 /* 59 * We need both IPv4 and IPv6 sockets to query both IPv4 and IPv6 60 * interfaces below. 61 */ 62 if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) == -1 || 63 (s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { 64 goto fail; 65 } 66 67 /* 68 * Get the number of network interfaces of type `family'. 69 */ 70 lifn.lifn_family = AF_UNSPEC; 71 lifn.lifn_flags = flags; 72 again: 73 if (ioctl(s4, SIOCGLIFNUM, &lifn) == -1) 74 goto fail; 75 76 /* 77 * Pad the interface count to detect when additional interfaces have 78 * been configured between SIOCGLIFNUM and SIOCGLIFCONF. 79 */ 80 lifn.lifn_count += 4; 81 82 lifc.lifc_flags = flags; 83 lifc.lifc_family = AF_UNSPEC; 84 lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq); 85 if ((lifc.lifc_buf = realloc(lifc.lifc_buf, lifc.lifc_len)) == NULL) 86 goto fail; 87 88 if (ioctl(s4, SIOCGLIFCONF, &lifc) == -1) 89 goto fail; 90 91 /* 92 * If every lifr_req slot is taken, then additional interfaces must 93 * have been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF. 94 * Recalculate to make sure we didn't miss any interfaces. 95 */ 96 nlifr = lifc.lifc_len / sizeof (struct lifreq); 97 if (nlifr >= lifn.lifn_count) 98 goto again; 99 100 /* 101 * Populate the ifaddrlistx by querying each matching interface. If a 102 * query ioctl returns ENXIO, then the interface must have been 103 * removed after the SIOCGLIFCONF completed -- so we just ignore it. 104 */ 105 for (lifrp = lifc.lifc_req, i = 0; i < nlifr; i++, lifrp++) { 106 if ((cp = strchr(lifrp->lifr_name, ':')) != NULL) 107 *cp = '\0'; 108 109 if (strcmp(lifrp->lifr_name, ifname) != 0) 110 continue; 111 112 if (cp != NULL) 113 *cp = ':'; 114 115 addr = lifrp->lifr_addr; 116 isv6 = addr.ss_family == AF_INET6; 117 if (ioctl(isv6 ? s6 : s4, SIOCGLIFFLAGS, lifrp) == -1) { 118 if (errno == ENXIO) 119 continue; 120 goto fail; 121 } 122 123 if (set != 0 && ((lifrp->lifr_flags & set) == 0) || 124 (lifrp->lifr_flags & clear) != 0) 125 continue; 126 127 /* 128 * We've got a match; allocate a new record. 129 */ 130 if ((ifaddrp = malloc(sizeof (ifaddrlistx_t))) == NULL) 131 goto fail; 132 133 (void) strlcpy(ifaddrp->ia_name, lifrp->lifr_name, LIFNAMSIZ); 134 ifaddrp->ia_flags = lifrp->lifr_flags; 135 ifaddrp->ia_addr = addr; 136 ifaddrp->ia_next = ifaddrs; 137 ifaddrs = ifaddrp; 138 naddr++; 139 } 140 141 (void) close(s4); 142 (void) close(s6); 143 free(lifc.lifc_buf); 144 *ifaddrsp = ifaddrs; 145 return (naddr); 146 fail: 147 save_errno = errno; 148 (void) close(s4); 149 (void) close(s6); 150 free(lifc.lifc_buf); 151 ifaddrlistx_free(ifaddrs); 152 errno = save_errno; 153 return (-1); 154 } 155 156 /* 157 * Free the provided ifaddrlistx_t. 158 */ 159 void 160 ifaddrlistx_free(ifaddrlistx_t *ifaddrp) 161 { 162 ifaddrlistx_t *next_ifaddrp; 163 164 for (; ifaddrp != NULL; ifaddrp = next_ifaddrp) { 165 next_ifaddrp = ifaddrp->ia_next; 166 free(ifaddrp); 167 } 168 } 169