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 }