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 2000 by Cisco Systems, Inc. All rights reserved. 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * iSCSI Software Initiator 27 */ 28 29 #include <sys/types.h> 30 #include <sys/errno.h> 31 #include <sys/conf.h> 32 #include <sys/cmn_err.h> 33 #include <sys/stat.h> 34 #include <sys/pathname.h> 35 #include <sys/door.h> 36 #include <sys/kmem.h> 37 #include <sys/socket.h> 38 #include <sys/fs/snode.h> 39 #include <netinet/in.h> 40 41 #include <sys/scsi/adapters/iscsi_door.h> 42 #include "iscsi.h" 43 44 #define ISCSI_DOOR_MAX_SEMA_VALUE 16 45 46 static boolean_t iscsi_door_init = B_FALSE; 47 static ksema_t iscsi_door_sema; 48 static krwlock_t iscsi_door_lock; 49 static door_handle_t iscsi_door_handle; 50 51 typedef struct _mybuffer { 52 size_t signature; 53 size_t size; 54 } mybuffer_t; 55 56 static 57 void 58 iscsi_door_unbind(void); 59 60 /* 61 * iscsi_door_ini 62 * 63 * This function initializes the variables needed to handle the door upcall. 64 */ 65 boolean_t 66 iscsi_door_ini(void) 67 { 68 ASSERT(!iscsi_door_init); 69 if (!iscsi_door_init) { 70 rw_init( 71 &iscsi_door_lock, 72 NULL, 73 RW_DRIVER, 74 NULL); 75 76 sema_init( 77 &iscsi_door_sema, 78 ISCSI_DOOR_MAX_SEMA_VALUE, 79 NULL, 80 SEMA_DRIVER, 81 NULL); 82 83 iscsi_door_handle = NULL; 84 iscsi_door_init = B_TRUE; 85 return (B_TRUE); 86 } 87 return (B_FALSE); 88 } 89 90 /* 91 * iscsi_door_term 92 * 93 * This function releases the resources allocated to handle the door 94 * upcall. It disconnects from the door if currently connected. 95 */ 96 boolean_t 97 iscsi_door_term(void) 98 { 99 ASSERT(iscsi_door_init); 100 if (iscsi_door_init) { 101 iscsi_door_init = B_FALSE; 102 iscsi_door_unbind(); 103 rw_destroy(&iscsi_door_lock); 104 sema_destroy(&iscsi_door_sema); 105 return (B_TRUE); 106 } 107 return (B_FALSE); 108 } 109 110 /* 111 * iscsi_door_bind 112 * 113 * This function tries to connect the iscsi_door. If it succeeds 114 * it keeps the vnode. 115 */ 116 boolean_t 117 iscsi_door_bind( 118 int did 119 ) 120 { 121 door_handle_t new_handle; 122 123 new_handle = door_ki_lookup(did); 124 if (new_handle == NULL) { 125 /* The lookup failed. */ 126 return (B_FALSE); 127 } 128 129 /* The new handle is stored. If we had one, it is released. */ 130 rw_enter(&iscsi_door_lock, RW_WRITER); 131 if (iscsi_door_handle != NULL) { 132 door_ki_rele(iscsi_door_handle); 133 } 134 iscsi_door_handle = new_handle; 135 rw_exit(&iscsi_door_lock); 136 137 return (B_TRUE); 138 } 139 140 /* 141 * iscsi_door_unbind 142 * 143 * This function releases the current door handle. 144 */ 145 static 146 void 147 iscsi_door_unbind(void) 148 { 149 rw_enter(&iscsi_door_lock, RW_WRITER); 150 if (iscsi_door_handle != NULL) { 151 door_ki_rele(iscsi_door_handle); 152 iscsi_door_handle = NULL; 153 } 154 rw_exit(&iscsi_door_lock); 155 } 156 157 /* 158 * iscsi_door_upcall 159 * 160 * This function tries to call the iscsi_door. 161 */ 162 static 163 boolean_t 164 iscsi_door_upcall(door_arg_t *arg) 165 { 166 int error; 167 168 /* 169 * This semaphore limits the number of simultaneous calls 170 * to the door. 171 */ 172 sema_p(&iscsi_door_sema); 173 /* 174 * The mutex protecting the iscsi_door_handle is entered. 175 */ 176 rw_enter(&iscsi_door_lock, RW_READER); 177 178 if (iscsi_door_handle == NULL) { 179 /* There's no door handle. */ 180 rw_exit(&iscsi_door_lock); 181 sema_v(&iscsi_door_sema); 182 return (B_FALSE); 183 } 184 error = door_ki_upcall(iscsi_door_handle, arg); 185 186 rw_exit(&iscsi_door_lock); 187 sema_v(&iscsi_door_sema); 188 189 if (error != 0) { 190 return (B_FALSE); 191 } else { 192 return (B_TRUE); 193 } 194 } 195 196 /* 197 * kfreehostent 198 * 199 * This function frees the memory returned by kgetipnodebyname. 200 */ 201 void 202 kfreehostent( 203 struct hostent *hptr 204 ) 205 { 206 mybuffer_t *buffer; 207 208 ASSERT(hptr != NULL); 209 if (hptr) { 210 buffer = (mybuffer_t *)((char *)hptr - sizeof (mybuffer_t)); 211 ASSERT(buffer->signature == ISCSI_DOOR_REQ_SIGNATURE); 212 if (buffer->signature == ISCSI_DOOR_REQ_SIGNATURE) { 213 kmem_free((void *)buffer, buffer->size); 214 return; 215 } 216 } 217 /* A message should be logged here. */ 218 } 219 220 /* 221 * kgetipnodebyname 222 * 223 * This function builds a request that will be sent to the iscsi_door. 224 * The iSCSI door after receiving the request calls getipnodebyaddr(). 225 * for more information on the input, output parameter and return value, 226 * consult the man page for getipnodebyname(). 227 * 228 * Before calling the iscsi door this function tries to do the conversion 229 * locally. If a name resolution is needed the iscsi door is called. 230 * 231 * There's some limitations to the information returned by this function. 232 * Only one address of the address list returned by getipnodebyname() is 233 * returned. The other parameters of the structure should be ignored. 234 */ 235 struct hostent * 236 kgetipnodebyname( 237 const char *name, 238 int af, 239 int flags, 240 int *error_num 241 ) 242 { 243 door_arg_t arg; 244 mybuffer_t *buffer; 245 size_t msg_size = ISCSI_DOOR_MAX_DATA_SIZE; 246 size_t hostent_size = ISCSI_DOOR_MAX_DATA_SIZE; 247 size_t buffer_size; 248 getipnodebyname_req_t *req; 249 getipnodebyname_cnf_t *cnf; 250 struct hostent *hptr; 251 int i; 252 uint16_t *swap; 253 254 255 buffer_size = msg_size + hostent_size + sizeof (mybuffer_t); 256 buffer = (mybuffer_t *)kmem_zalloc(buffer_size, KM_SLEEP); 257 258 if (buffer) { 259 260 /* 261 * The buffer was successfully allocated. 262 * 263 * Buffer 264 * 265 * +--------------------+ <--- buffer 266 * | mybuffer_t | 267 * +--------------------+ <--- hptr 268 * | | 269 * | | 270 * | hostent_size | 271 * | | 272 * | | 273 * | | 274 * +--------------------+ <--- req, cnf 275 * | | 276 * | | 277 * | | 278 * | msg_size | 279 * | | 280 * | | 281 * | | 282 * +--------------------+ 283 */ 284 buffer->signature = ISCSI_DOOR_REQ_SIGNATURE; 285 buffer->size = buffer_size; 286 287 hptr = (struct hostent *)((char *)buffer + sizeof (mybuffer_t)); 288 req = (getipnodebyname_req_t *)((char *)hptr + hostent_size); 289 cnf = (getipnodebyname_cnf_t *)((char *)hptr + hostent_size); 290 291 hostent_size -= sizeof (struct hostent); 292 293 /* 294 * We try first locally. If the conversion cannot be done 295 * by inet_pton the door is called. 296 * The cnf address is used as output buffer. 297 * inet_pton returns '1' if the conversion was successful. 298 */ 299 switch (af) { 300 case AF_INET: 301 hptr->h_length = sizeof (struct in_addr); 302 break; 303 case AF_INET6: 304 hptr->h_length = sizeof (struct in6_addr); 305 break; 306 default: 307 kfreehostent(hptr); 308 *error_num = NO_RECOVERY; 309 return (NULL); 310 } 311 if ((msg_size < hptr->h_length) || 312 (hostent_size < sizeof (char *))) { 313 kfreehostent(hptr); 314 *error_num = NO_RECOVERY; 315 return (NULL); 316 } 317 if (inet_pton(af, (char *)name, cnf) == 1) { 318 /* 319 * inet_pton converted the string successfully. 320 * reset to network order. swaps based on nfs code 321 */ 322 if (af == AF_INET) { 323 *((uint32_t *)cnf) = htonl(*((uint32_t *)cnf)); 324 } else { 325 for (swap = ((void *)cnf), i = 0; 326 i < hptr->h_length / sizeof (uint16_t); 327 i++) { 328 swap[i] = htons(swap[i]); 329 } 330 } 331 hptr->h_addrtype = af; 332 hptr->h_addr_list = (char **)((char *)hptr + 333 sizeof (struct hostent)); 334 *hptr->h_addr_list = (char *)cnf; 335 return (hptr); 336 } 337 338 /* 339 * The name couldn't ne converted by inet_pton. The door is 340 * called. 341 */ 342 343 /* Header initialization. */ 344 req->hdr.signature = ISCSI_DOOR_REQ_SIGNATURE; 345 req->hdr.version = ISCSI_DOOR_REQ_VERSION_1; 346 req->hdr.opcode = ISCSI_DOOR_GETIPNODEBYNAME_REQ; 347 348 /* Body initialization. */ 349 req->name_length = strlen(name); 350 if (req->name_length > 351 (msg_size - sizeof (getipnodebyname_req_t) - 1)) { 352 kfreehostent(hptr); 353 *error_num = NO_RECOVERY; 354 return (NULL); 355 } 356 357 req->name_offset = sizeof (getipnodebyname_req_t); 358 req->af = af; 359 req->flags = flags; 360 bcopy( 361 name, 362 ((char *)req + req->name_offset), 363 req->name_length); 364 365 /* Door argument initialization. */ 366 arg.data_ptr = (char *)req; 367 arg.data_size = msg_size; 368 arg.desc_num = 0; 369 arg.desc_ptr = NULL; 370 arg.rbuf = (char *)cnf; 371 arg.rsize = msg_size; 372 373 if (iscsi_door_upcall(&arg) == B_FALSE) { 374 /* The door call failed */ 375 kfreehostent(hptr); 376 *error_num = NO_RECOVERY; 377 return (NULL); 378 } 379 380 /* 381 * The door call itself was successful. The value returned 382 * in arg.rbuf should be cnf, but we never know. 383 */ 384 cnf = (getipnodebyname_cnf_t *)arg.rbuf; 385 386 if ((cnf == NULL) || 387 (arg.rsize < sizeof (getipnodebyname_cnf_t)) || 388 (cnf->hdr.signature != ISCSI_DOOR_REQ_SIGNATURE) || 389 (cnf->hdr.version != ISCSI_DOOR_REQ_VERSION_1) || 390 (cnf->hdr.opcode != ISCSI_DOOR_GETIPNODEBYNAME_CNF) || 391 ((cnf->hdr.status != ISCSI_DOOR_STATUS_SUCCESS) && 392 (cnf->hdr.status != ISCSI_DOOR_STATUS_MORE))) { 393 /* The door didn't like the request */ 394 kfreehostent(hptr); 395 *error_num = NO_RECOVERY; 396 return (NULL); 397 } 398 399 if (cnf->h_addr_list_length == 0) { 400 kfreehostent(hptr); 401 *error_num = HOST_NOT_FOUND; 402 return (NULL); 403 } 404 405 hptr->h_addrtype = cnf->h_addrtype; 406 hptr->h_length = cnf->h_addrlen; 407 hptr->h_addr_list = (char **)((char *)hptr + 408 sizeof (struct hostent)); 409 *hptr->h_addr_list = ((char *)cnf + cnf->h_addr_list_offset); 410 return (hptr); 411 } else { 412 *error_num = NO_RECOVERY; 413 return (NULL); 414 } 415 } 416