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