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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <sys/types.h>
  26 #include <sys/conf.h>
  27 #include <sys/modctl.h>
  28 #include <sys/stat.h>
  29 #include <sys/stream.h>
  30 #include <sys/strsun.h>
  31 #include <sys/stropts.h>
  32 #include <sys/ddi.h>
  33 #include <sys/sunddi.h>
  34 #include <sys/sunldi.h>
  35 #include <sys/file.h>
  36 #include <sys/priv_names.h>
  37 #include <inet/common.h>
  38 
  39 #define _SUN_TPI_VERSION 2
  40 #include <sys/tihdr.h>
  41 #include <sys/timod.h>
  42 #include <sys/tiuser.h>
  43 #include <sys/suntpi.h>
  44 #include <sys/socket.h>
  45 #include <sys/sockio.h>
  46 #include <inet/common.h>
  47 #include <inet/ip.h>
  48 #include <inet/mi.h>
  49 #include <sys/policy.h>
  50 #include "sys/random.h"
  51 #include <inet/sdp_itf.h>
  52 #include <sys/ib/ibtl/ibti.h>
  53 
  54 
  55 /*
  56  * This is a pseudo driver which creates an entry for /dev/sdp in the device
  57  * tree. A regular installation will end up adding a file to sock2path.d
  58  * announcing support for sdp using AF_INET/SOCK_STREAM/PROTO_SDP parameters in
  59  * socket call. On a non IB hardware, following are the constraints within
  60  * which the sdp project operates. The sdpib driver which is the real driver
  61  * (in terms of moving data) should not be loaded since it has dependency on
  62  * ibcm and ibtl modules which will be loaded in the memory. This will consume
  63  * precious memory and needs to be avoided. As a result the sdpib driver
  64  * should fail its init() call to disallow loading on other modules. Due to
  65  * this we do not get a chance to create a /dev/sdp entry in the device tree
  66  * in the regular sdpib driver. During the boottime, this will cause a warning
  67  * message when  soconfig processes the entry for sdp in sock2path file . In
  68  * order to avoid this a pseudo driver is introduced which creates an entry
  69  * for /dev/sdp regardless of the hardware. When a socket  call is made on the
  70  * sdp subsystem, the call will end up in this driver, which then forwards
  71  * this call to the real sdp driver.  On a non-ib hardware system the call
  72  * will fail
  73  */
  74 
  75 #define SDP_NAME        "sdp"
  76 #define SDP_DEVDESC     "SDP STREAMS driver"
  77 #define SDP_DEVMINOR    0
  78 
  79 static dev_info_t *sdp_dev_info;
  80 
  81 ldi_ident_t sdp_li;
  82 krwlock_t       sdp_transport_lock;
  83 ldi_handle_t    sdp_transport_handle = NULL;
  84 
  85 static int
  86 sdp_gen_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
  87 {
  88         int     ret;
  89 
  90         if (cmd != DDI_ATTACH)
  91                 return (DDI_FAILURE);
  92 
  93         sdp_dev_info = devi;
  94 
  95         ret = ddi_create_minor_node(devi, SDP_NAME, S_IFCHR,
  96             SDP_DEVMINOR, DDI_PSEUDO, 0);
  97         if (ret != DDI_SUCCESS) {
  98                 return (ret);
  99         }
 100         return (DDI_SUCCESS);
 101 }
 102 
 103 static int
 104 sdp_gen_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 105 {
 106         if (cmd != DDI_DETACH)
 107                 return (DDI_FAILURE);
 108 
 109         ASSERT(devi == sdp_dev_info);
 110 
 111         ddi_remove_minor_node(devi, NULL);
 112 
 113         return (DDI_SUCCESS);
 114 }
 115 
 116 /* open routine. */
 117 /*ARGSUSED*/
 118 static int
 119 sdp_gen_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
 120 {
 121         qprocson(q);
 122         qenable(q);
 123         return (0);
 124 }
 125 
 126 /* open routine. */
 127 /*ARGSUSED*/
 128 static int
 129 sdp_gen_close(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
 130 {
 131         qprocsoff(q);
 132         return (0);
 133 }
 134 
 135 static int
 136 sdp_open_sdpib_driver()
 137 {
 138         int ret = 0;
 139 
 140         rw_enter(&sdp_transport_lock, RW_WRITER);
 141         if (sdp_transport_handle != 0) {
 142                 /*
 143                  * Someone beat us to it.
 144                  */
 145                 goto done;
 146         }
 147 
 148         if (ibt_hw_is_present() == 0) {
 149                 ret = ENODEV;
 150                 goto done;
 151         }
 152 
 153         if (sdp_li == NULL) {
 154                 ret = EPROTONOSUPPORT;
 155                 goto done;
 156         }
 157 
 158         ret = ldi_open_by_name("/devices/ib/sdpib@0:sdpib",
 159             FREAD | FWRITE, kcred, &sdp_transport_handle, sdp_li);
 160         if (ret != 0) {
 161                 ret = EPROTONOSUPPORT;
 162                 sdp_transport_handle = NULL;
 163                 goto done;
 164         }
 165 
 166 done:
 167         rw_exit(&sdp_transport_lock);
 168         return (ret);
 169 }
 170 
 171 
 172 static void
 173 sdp_gen_ioctl(queue_t *q, mblk_t *mp)
 174 {
 175         struct iocblk *iocp;
 176         int32_t enable = 0;
 177         int ret;
 178         boolean_t priv = B_TRUE;
 179 
 180         /* LINTED */
 181         iocp = (struct iocblk *)mp->b_rptr;
 182         switch (iocp->ioc_cmd) {
 183                         int32_t send_enable;
 184                 case SIOCSENABLESDP:
 185                         bcopy(mp->b_cont->b_rptr, &enable, sizeof (int));
 186 
 187                         send_enable = enable;
 188 
 189                         /*
 190                          * Check for root privs.
 191                          * if not net config privs - return state of system SDP
 192                          */
 193                         if (secpolicy_net_config(CRED(), B_FALSE) != 0) {
 194                                 priv = B_FALSE;
 195                         }
 196 
 197 
 198                         /*
 199                          * The sdpib driver is loaded if root enables sdp the
 200                          * first time (sdp_transport_handle is NULL). It is
 201                          * unloaded during the following first disable. At all
 202                          * other times for root as well as non-root users, the
 203                          * action of enabling/disabling sdp is simply acked.
 204                          */
 205                         rw_enter(&sdp_transport_lock, RW_READER);
 206                         if ((send_enable == 1) &&
 207                             (sdp_transport_handle == NULL) &&
 208                             (priv == B_TRUE)) {
 209                                 /* Initialize sdpib transport driver */
 210                                 rw_exit(&sdp_transport_lock);
 211                                 ret = sdp_open_sdpib_driver();
 212                                 rw_enter(&sdp_transport_lock,
 213                                     RW_READER);
 214                                 if (ret != 0) {
 215                                         /* Transport failed to load */
 216                                         rw_exit(&sdp_transport_lock);
 217                                         enable = 0;
 218                                         goto done;
 219                                 }
 220                                 (void) ldi_ioctl(sdp_transport_handle,
 221                                     iocp->ioc_cmd, (intptr_t)&send_enable,
 222                                     FKIOCTL, CRED(), (int *)&enable);
 223                         } else if (sdp_transport_handle != NULL) {
 224                                 (void) ldi_ioctl(sdp_transport_handle,
 225                                     iocp->ioc_cmd, (intptr_t)&send_enable,
 226                                     FKIOCTL, CRED(), (int *)&enable);
 227                                 if (send_enable == 0 && priv == B_TRUE) {
 228                                         (void) ldi_close(sdp_transport_handle,
 229                                             FNDELAY, kcred);
 230                                         sdp_transport_handle = NULL;
 231                                 }
 232                         } else {
 233                                 enable = 0;
 234                         }
 235                         rw_exit(&sdp_transport_lock);
 236 
 237 done:
 238                         bcopy(&enable, mp->b_cont->b_rptr, sizeof (int));
 239 
 240                         /* ACK the ioctl */
 241                         mp->b_datap->db_type = M_IOCACK;
 242                         iocp->ioc_count = sizeof (int);
 243                         qreply(q, mp);
 244                         break;
 245                 default:
 246                         miocnak(q, mp, 0, ENOTSUP);
 247         }
 248 }
 249 
 250 /*
 251  * Received a put from sockfs. We only support ndd get/set
 252  */
 253 static void
 254 sdp_gen_wput(queue_t *q, mblk_t *mp)
 255 {
 256         switch (mp->b_datap->db_type) {
 257         case M_IOCTL:
 258                 sdp_gen_ioctl(q, mp);
 259                 break;
 260         case M_FLUSH:
 261                 *mp->b_rptr &= ~FLUSHW;
 262                 if (*mp->b_rptr & FLUSHR)
 263                         qreply(q, mp);
 264                 else
 265                         freemsg(mp);
 266                 break;
 267         default:
 268                 freemsg(mp);
 269                 return;
 270         }
 271 }
 272 
 273 static struct module_info info = {
 274         0, "sdp", 1, INFPSZ, 65536, 1024
 275 };
 276 
 277 static struct qinit rinit = {
 278         NULL, (pfi_t)NULL, (pfi_t)sdp_gen_open, (pfi_t)sdp_gen_close, NULL,
 279         &info, NULL, NULL, NULL, STRUIOT_NONE
 280 };
 281 
 282 static struct qinit winit = {
 283         (pfi_t)sdp_gen_wput, NULL,  (pfi_t)sdp_gen_open,  (pfi_t)sdp_gen_close,
 284         NULL, &info, NULL, NULL, NULL, STRUIOT_NONE
 285 };
 286 
 287 struct streamtab sdpinfo = {
 288         &rinit, &winit, NULL, NULL
 289 };
 290 
 291 DDI_DEFINE_STREAM_OPS(sdp_devops, nulldev, nulldev, sdp_gen_attach,
 292     sdp_gen_detach, nodev, NULL, D_MP, &sdpinfo, ddi_quiesce_not_needed);
 293 
 294 /*
 295  * Module linkage information for the kernel.
 296  */
 297 static struct modldrv modldrv = {
 298         &mod_driverops,
 299         SDP_DEVDESC,
 300         &sdp_devops
 301 };
 302 
 303 static struct modlinkage modlinkage = {
 304         MODREV_1,
 305         { &modldrv, NULL }
 306 };
 307 
 308 int
 309 _init(void)
 310 {
 311         int     ret;
 312 
 313         ret = mod_install(&modlinkage);
 314         if (ret != 0)
 315                 goto done;
 316         ret = ldi_ident_from_mod(&modlinkage, &sdp_li);
 317         if (ret != 0)
 318                 sdp_li = NULL;
 319 done:
 320         return (ret);
 321 }
 322 
 323 int
 324 _fini(void)
 325 {
 326         int     ret;
 327 
 328         ret = mod_remove(&modlinkage);
 329         if (ret != 0) {
 330                 return (ret);
 331         }
 332 
 333         ldi_ident_release(sdp_li);
 334         return (0);
 335 }
 336 
 337 int
 338 _info(struct modinfo *modinfop)
 339 {
 340         return (mod_info(&modlinkage, modinfop));
 341 }