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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <libdlpi.h> 29 #include <ctype.h> 30 #include <sys/sysmacros.h> 31 #include <net/if_types.h> 32 #include <net/if.h> 33 #include <sys/types.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <net/if_dl.h> 37 #include <sys/sockio.h> 38 #include <sys/socket.h> 39 #include <unistd.h> 40 #include <strings.h> 41 #include <stropts.h> 42 #include <fcntl.h> 43 #include <netinet/in.h> 44 #include <arpa/inet.h> 45 46 #include <iscsitgt_impl.h> 47 #include "target.h" 48 #include "queue.h" 49 #include "utility.h" 50 51 #define LOCAL_LOOPBACK "lo0" 52 53 /* 54 * This entire file is all about getting these two variables. To create a 55 * unique iSCSI IQN string we need information that is unique. What Sun has 56 * decided is to use a MAC address along with a timestamp. No other machine 57 * at this given time will have the same MAC address and as time moves along 58 * well, time will change. 59 */ 60 uchar_t mac_addr[DLPI_PHYSADDR_MAX]; 61 size_t mac_len; 62 63 static struct lifreq *if_setup(int *n); 64 static void dump_addr_to_ascii(struct sockaddr *addr, char *buf, 65 size_t len); 66 static Boolean_t grab_address(char *ifname, uchar_t *addrp, size_t *addrlenp); 67 68 /* 69 * []---- 70 * | if_find_mac -- Finds a valid MAC address to use for GUID & IQN creation 71 * | 72 * | To create both the GUID and the IQN string we need to make them unique 73 * | and do so without requiring the user to have to register each target 74 * | creation with Sun. Each machine that's using iSCSI will have a network 75 * | interface from which we can obtain the MAC address. That guarantees 76 * | uniqueness within the network, but doesn't guarantee uniqueness with 77 * | the machine. So when creating the GUID/IQN we also use a timestamp. 78 * []---- 79 */ 80 Boolean_t 81 if_find_mac(target_queue_t *mgmt) 82 { 83 struct lifreq *lifrp, *first; 84 int n; 85 char *str; 86 87 mac_len = DLPI_PHYSADDR_MAX; 88 89 first = if_setup(&n); 90 for (lifrp = first; n > 0; n--, lifrp++) { 91 if (grab_address(lifrp->lifr_name, mac_addr, 92 &mac_len) == True) { 93 str = _link_ntoa(mac_addr, NULL, mac_len, IFT_OTHER); 94 if ((str != NULL) && (mgmt != NULL)) { 95 queue_prt(mgmt, Q_GEN_DETAILS, 96 "MAIN %s: %s \n", lifrp->lifr_name, str); 97 free(str); 98 } 99 /* ---- grab the first valid MAC address ---- */ 100 break; 101 } 102 } 103 if (first) 104 free(first); 105 return (mac_len == 0 ? False : True); 106 } 107 108 /* 109 * []---- 110 * | if_target_address -- setup IP address for SendTargets 111 * | 112 * | This routine is called when the iSCSI target is returning SendTargets 113 * | data during a discovery phase. The target name is returned along 114 * | with all of the IP address that can access that target. There's one 115 * | catch, the first address in the list will be the address used by 116 * | the initiator if it doesn't support multiple connections per session. 117 * | Therefore, whatever connection the initiator used is the first one 118 * | that should be in our list. The ramificiations of not doing this are 119 * | possible performance issues. Take for example a setup where both the 120 * | initiator and target have 10GbE and 1GbE interfaces. The initiator wants 121 * | to use the 10GbE interface because of it's speed. If the target returns 122 * | a list of addresses with the 1GbE listed first, that's the one which 123 * | the initiator would use. Not good. 124 * []---- 125 */ 126 void 127 if_target_address(char **text, int *text_length, struct sockaddr *sp) 128 { 129 struct lifreq *lp, *first; 130 int n, i, s; 131 struct sockaddr_in *sin4_cur, *sin4_pos; 132 struct sockaddr_in6 *sin6_cur, *sin6_pos; 133 char ta[80], ip_buf[INET6_ADDRSTRLEN]; 134 int fromlen; 135 136 if (sp->sa_family == AF_INET) { 137 fromlen = sizeof (struct sockaddr_in); 138 139 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 140 return; 141 142 /*LINTED*/ 143 sin4_cur = (struct sockaddr_in *)sp; 144 145 /* 146 * Ugh. Would you believe that even though this array 147 * is defined as zero, we get back non-zero data from 148 * getsockname(). 149 */ 150 bzero(&sin4_cur->sin_zero[0], sizeof (sin4_cur->sin_zero)); 151 152 } else if (sp->sa_family == AF_INET6) { 153 fromlen = sizeof (struct sockaddr_in6); 154 155 if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) 156 return; 157 158 /*LINTED*/ 159 sin6_cur = (struct sockaddr_in6 *)sp; 160 } else 161 return; 162 163 first = if_setup(&n); 164 for (lp = first, i = 0; i < n; i++, lp++) { 165 166 if (sp->sa_family != lp->lifr_addr.ss_family) 167 continue; 168 169 /* 170 * Change the possible incoming addresses port number, 171 * which would be zero, to that of the current incoming 172 * port number. Otherwise the comparison will not match. 173 */ 174 if (sp->sa_family == AF_INET) { 175 sin4_pos = (struct sockaddr_in *)&lp->lifr_addr; 176 sin4_pos->sin_port = sin4_cur->sin_port; 177 } else if (sp->sa_family == AF_INET6) { 178 sin6_pos = (struct sockaddr_in6 *)&lp->lifr_addr; 179 sin6_pos->sin6_port = sin6_cur->sin6_port; 180 } else 181 goto clean_up; 182 } 183 184 for (lp = first, i = 0; i < n; i++, lp++) { 185 186 if (bcmp(sp, &lp->lifr_addr, fromlen) == 0) { 187 dump_addr_to_ascii((struct sockaddr *)&lp->lifr_addr, 188 ip_buf, sizeof (ip_buf)); 189 190 if (sp->sa_family == AF_INET) { 191 (void) snprintf(ta, sizeof (ta), "%s,1", 192 ip_buf); 193 } else if (sp->sa_family == AF_INET6) { 194 (void) snprintf(ta, sizeof (ta), "[%s],1", 195 ip_buf); 196 } else 197 goto clean_up; 198 199 (void) add_text(text, text_length, "TargetAddress", ta); 200 201 /* 202 * There is possiblity that both IPv4 & IPv6 enabled on 203 * certain interface, then we will see that interface 204 * twice identically in the list. 205 * Of course we need only one of them, not both. 206 */ 207 break; 208 } 209 } 210 211 for (lp = first, i = 0; i < n; i++, lp++) { 212 /* 213 * We allow for the loopback address to match the discovery 214 * address above since it's entirely possible to create 215 * a target on the same machine that you're running the 216 * initiator. Now, when we provide the list of other 217 * possible interfaces to use we don't want to include 218 * the loopback because that's obviously not a valid I/F 219 * for a remote node. 220 */ 221 if (strcmp(lp->lifr_name, LOCAL_LOOPBACK) == 0) 222 continue; 223 224 if (bcmp(sp, &lp->lifr_addr, fromlen) != 0) { 225 struct sockaddr *sp2; 226 sp2 = (struct sockaddr *)&lp->lifr_addr; 227 dump_addr_to_ascii(sp2, ip_buf, sizeof (ip_buf)); 228 229 if (sp2->sa_family == AF_INET) { 230 (void) snprintf(ta, sizeof (ta), "%s,1", 231 ip_buf); 232 } else if (sp2->sa_family == AF_INET6) { 233 (void) snprintf(ta, sizeof (ta), "[%s],1", 234 ip_buf); 235 } else 236 goto clean_up; 237 (void) add_text(text, text_length, "TargetAddress", ta); 238 } 239 } 240 241 clean_up: 242 (void) close(s); 243 if (first) 244 free(first); 245 } 246 247 /* 248 * []---- 249 * | dump_addr_to_ascii -- Use appropriate translation routine 250 * []---- 251 */ 252 static void 253 dump_addr_to_ascii(struct sockaddr *addr, char *buf, size_t len) 254 { 255 struct sockaddr_in *sin4; 256 struct sockaddr_in6 *sin6; 257 258 if (addr->sa_family == AF_INET) { 259 /*LINTED*/ 260 sin4 = (struct sockaddr_in *)addr; 261 (void) inet_ntop(AF_INET, &sin4->sin_addr, buf, len); 262 } else if (addr->sa_family == AF_INET6) { 263 /*LINTED*/ 264 sin6 = (struct sockaddr_in6 *)addr; 265 (void) inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len); 266 } 267 } 268 269 /* 270 * []---- 271 * | if_setup -- Load up the interface names 272 * | 273 * | If this routine returns NULL, argument 'n' is also guaranteed to 274 * | be set to 0. 275 * []---- 276 */ 277 static struct lifreq * 278 if_setup(int *n) 279 { 280 struct lifnum lifn; 281 struct lifconf lifc; 282 int numifs; 283 unsigned bufsize; 284 char *buf; 285 int s; 286 287 *n = 0; 288 if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { 289 /* 290 * If we failed to open an IPv6 socket 291 * try IPv4 socket instead 292 */ 293 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 294 return (NULL); 295 } 296 297 lifn.lifn_family = AF_UNSPEC; 298 lifn.lifn_flags = LIFC_ALLZONES | LIFC_EXTERNAL_SOURCE; 299 if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) 300 return (NULL); 301 302 numifs = lifn.lifn_count; 303 304 bufsize = numifs * sizeof (struct lifreq); 305 if ((buf = malloc(bufsize)) == NULL) { 306 /* 307 * This call is made so early on that if we're out of memory 308 * here, just say goodbye. 309 */ 310 return (NULL); 311 } 312 313 lifc.lifc_family = AF_UNSPEC; 314 lifc.lifc_flags = LIFC_ALLZONES | LIFC_EXTERNAL_SOURCE; 315 lifc.lifc_len = bufsize; 316 lifc.lifc_buf = buf; 317 318 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { 319 free(buf); 320 return (NULL); 321 } 322 323 (void) close(s); 324 *n = lifc.lifc_len / sizeof (struct lifreq); 325 return (lifc.lifc_req); 326 } 327 328 static Boolean_t 329 grab_address(char *ifname, uchar_t *addrp, size_t *addrlenp) 330 { 331 int retval; 332 dlpi_handle_t dh; 333 334 if (dlpi_open(ifname, &dh, 0) != DLPI_SUCCESS) 335 return (False); 336 337 retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, addrp, addrlenp); 338 339 dlpi_close(dh); 340 341 return (retval == DLPI_SUCCESS); 342 } 343