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