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 }