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, 306 NULL 307 }; 308 309 int 310 _init(void) 311 { 312 int ret; 313 314 ret = mod_install(&modlinkage); 315 if (ret != 0) 316 goto done; 317 ret = ldi_ident_from_mod(&modlinkage, &sdp_li); 318 if (ret != 0) 319 sdp_li = NULL; 320 done: 321 return (ret); 322 } 323 324 int 325 _fini(void) 326 { 327 int ret; 328 329 ret = mod_remove(&modlinkage); 330 if (ret != 0) { 331 return (ret); 332 } 333 334 ldi_ident_release(sdp_li); 335 return (0); 336 } 337 338 int 339 _info(struct modinfo *modinfop) 340 { 341 return (mod_info(&modlinkage, modinfop)); 342 }