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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/param.h> 31 #include <sys/cpuvar.h> 32 #include <sys/ddi.h> 33 #include <sys/sunddi.h> 34 #include <sys/time.h> 35 #include <sys/varargs.h> 36 #include <sys/modctl.h> 37 #include <sys/pathname.h> 38 #include <sys/fs/snode.h> 39 #include <sys/fs/dv_node.h> 40 #include <sys/vnode.h> 41 #undef mem_free /* XXX Remove this after we convert everything to kmem_alloc */ 42 43 #include <smbsrv/smb_vops.h> 44 #include <smbsrv/smb.h> 45 #include <smbsrv/mlsvc.h> 46 #include <smbsrv/smb_kproto.h> 47 #include <smbsrv/smb_kstat.h> 48 49 static kmem_cache_t *smb_txr_cache = NULL; 50 51 /* 52 * smb_net_init 53 * 54 * This function initializes the resources necessary to access the 55 * network. It assumes it won't be called simultaneously by multiple 56 * threads. 57 * 58 * Return Value 59 * 60 * 0 Initialization successful 61 * ENOMEM Initialization failed 62 */ 63 int 64 smb_net_init(void) 65 { 66 int rc = 0; 67 68 ASSERT(smb_txr_cache == NULL); 69 70 smb_txr_cache = kmem_cache_create(SMBSRV_KSTAT_TXRCACHE, 71 sizeof (smb_txreq_t), 8, NULL, NULL, NULL, NULL, NULL, 0); 72 if (smb_txr_cache == NULL) 73 rc = ENOMEM; 74 return (rc); 75 } 76 77 /* 78 * smb_net_fini 79 * 80 * This function releases the resources allocated by smb_net_init(). It 81 * assumes it won't be called simultaneously by multiple threads. 82 * This function can safely be called even if smb_net_init() hasn't been 83 * called previously. 84 * 85 * Return Value 86 * 87 * None 88 */ 89 void 90 smb_net_fini(void) 91 { 92 if (smb_txr_cache) { 93 kmem_cache_destroy(smb_txr_cache); 94 smb_txr_cache = NULL; 95 } 96 } 97 98 /* 99 * SMB Network Socket API 100 * 101 * smb_socreate: Creates an socket based on domain/type. 102 * smb_soshutdown: Disconnect a socket created with smb_socreate 103 * smb_sodestroy: Release resources associated with a socket 104 * smb_sosend: Send the contents of a buffer on a socket 105 * smb_sorecv: Receive data into a buffer from a socket 106 * smb_iov_sosend: Send the contents of an iovec on a socket 107 * smb_iov_sorecv: Receive data into an iovec from a socket 108 */ 109 110 struct sonode * 111 smb_socreate(int domain, int type, int protocol) 112 { 113 vnode_t *dvp = NULL; 114 vnode_t *vp = NULL; 115 struct snode *csp = NULL; 116 int err = 0; 117 major_t maj; 118 119 if ((vp = solookup(domain, type, protocol, NULL, &err)) == NULL) { 120 121 /* 122 * solookup calls sogetvp if the vp is not found in the cache. 123 * Since the call to sogetvp is hardwired to use USERSPACE 124 * and declared static we'll do the work here instead. 125 */ 126 err = lookupname(type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp", 127 UIO_SYSSPACE, FOLLOW, NULLVPP, &vp); 128 if (err) 129 return (NULL); 130 131 /* Check that it is the correct vnode */ 132 if (vp->v_type != VCHR) { 133 VN_RELE(vp); 134 return (NULL); 135 } 136 137 csp = VTOS(VTOS(vp)->s_commonvp); 138 if (!(csp->s_flag & SDIPSET)) { 139 char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 140 err = ddi_dev_pathname(vp->v_rdev, S_IFCHR, 141 pathname); 142 if (err == 0) { 143 err = devfs_lookupname(pathname, NULLVPP, 144 &dvp); 145 } 146 VN_RELE(vp); 147 kmem_free(pathname, MAXPATHLEN); 148 if (err != 0) { 149 return (NULL); 150 } 151 vp = dvp; 152 } 153 154 maj = getmajor(vp->v_rdev); 155 if (!STREAMSTAB(maj)) { 156 VN_RELE(vp); 157 return (NULL); 158 } 159 } 160 161 return (socreate(vp, domain, type, protocol, SOV_DEFAULT, NULL, &err)); 162 } 163 164 /* 165 * smb_soshutdown will disconnect the socket and prevent subsequent PDU 166 * reception and transmission. The sonode still exists but its state 167 * gets modified to indicate it is no longer connected. Calls to 168 * smb_sorecv/smb_iov_sorecv will return so smb_soshutdown can be used 169 * regain control of a thread stuck in smb_sorecv. 170 */ 171 void 172 smb_soshutdown(struct sonode *so) 173 { 174 (void) soshutdown(so, SHUT_RDWR); 175 } 176 177 /* 178 * smb_sodestroy releases all resources associated with a socket previously 179 * created with smb_socreate. The socket must be shutdown using smb_soshutdown 180 * before the socket is destroyed with smb_sodestroy, otherwise undefined 181 * behavior will result. 182 */ 183 void 184 smb_sodestroy(struct sonode *so) 185 { 186 vnode_t *vp = SOTOV(so); 187 188 (void) VOP_CLOSE(vp, 0, 1, 0, kcred, NULL); 189 VN_RELE(vp); 190 } 191 192 int 193 smb_sorecv(struct sonode *so, void *msg, size_t len) 194 { 195 iovec_t iov; 196 int err; 197 198 ASSERT(so != NULL); 199 ASSERT(len != 0); 200 201 /* 202 * Fill in iovec and receive data 203 */ 204 iov.iov_base = msg; 205 iov.iov_len = len; 206 207 if ((err = smb_iov_sorecv(so, &iov, 1, len)) != 0) { 208 return (err); 209 } 210 211 /* Successful receive */ 212 return (0); 213 } 214 215 /* 216 * smb_iov_sorecv - Receives an iovec from a connection 217 * 218 * This function gets the data asked for from the socket. It will return 219 * only when all the requested data has been retrieved or if an error 220 * occurs. 221 * 222 * Returns 0 for success, the socket errno value if sorecvmsg fails, and 223 * -1 if sorecvmsg returns success but uio_resid != 0 224 */ 225 int 226 smb_iov_sorecv(struct sonode *so, iovec_t *iop, int iovlen, size_t total_len) 227 { 228 struct msghdr msg; 229 struct uio uio; 230 int error; 231 232 ASSERT(iop != NULL); 233 234 /* Initialization of the message header. */ 235 bzero(&msg, sizeof (msg)); 236 msg.msg_iov = iop; 237 msg.msg_flags = MSG_WAITALL; 238 msg.msg_iovlen = iovlen; 239 240 /* Initialization of the uio structure. */ 241 bzero(&uio, sizeof (uio)); 242 uio.uio_iov = iop; 243 uio.uio_iovcnt = iovlen; 244 uio.uio_segflg = UIO_SYSSPACE; 245 uio.uio_resid = total_len; 246 247 if ((error = sorecvmsg(so, &msg, &uio)) == 0) { 248 /* Received data */ 249 if (uio.uio_resid == 0) { 250 /* All requested data received. Success */ 251 return (0); 252 } else { 253 /* Not all data was sent. Failure */ 254 return (-1); 255 } 256 } 257 258 /* Receive failed */ 259 return (error); 260 } 261 262 /* 263 * smb_net_txl_constructor 264 * 265 * Transmit list constructor 266 */ 267 void 268 smb_net_txl_constructor(smb_txlst_t *txl) 269 { 270 ASSERT(txl->tl_magic != SMB_TXLST_MAGIC); 271 272 mutex_init(&txl->tl_mutex, NULL, MUTEX_DEFAULT, NULL); 273 list_create(&txl->tl_list, sizeof (smb_txreq_t), 274 offsetof(smb_txreq_t, tr_lnd)); 275 txl->tl_active = B_FALSE; 276 txl->tl_magic = SMB_TXLST_MAGIC; 277 } 278 279 /* 280 * smb_net_txl_destructor 281 * 282 * Transmit list destructor 283 */ 284 void 285 smb_net_txl_destructor(smb_txlst_t *txl) 286 { 287 ASSERT(txl->tl_magic == SMB_TXLST_MAGIC); 288 289 txl->tl_magic = 0; 290 list_destroy(&txl->tl_list); 291 mutex_destroy(&txl->tl_mutex); 292 } 293 294 /* 295 * smb_net_txr_alloc 296 * 297 * Transmit buffer allocator 298 */ 299 smb_txreq_t * 300 smb_net_txr_alloc(void) 301 { 302 smb_txreq_t *txr; 303 304 txr = kmem_cache_alloc(smb_txr_cache, KM_SLEEP); 305 txr->tr_len = 0; 306 bzero(&txr->tr_lnd, sizeof (txr->tr_lnd)); 307 txr->tr_magic = SMB_TXREQ_MAGIC; 308 return (txr); 309 } 310 311 /* 312 * smb_net_txr_free 313 * 314 * Transmit buffer deallocator 315 */ 316 void 317 smb_net_txr_free(smb_txreq_t *txr) 318 { 319 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC); 320 ASSERT(!list_link_active(&txr->tr_lnd)); 321 322 txr->tr_magic = 0; 323 kmem_cache_free(smb_txr_cache, txr); 324 } 325 326 /* 327 * smb_net_txr_send 328 * 329 * This routine puts the transmit buffer passed in on the wire. If another 330 * thread is already draining the transmit list, the transmit buffer is 331 * queued and the routine returns immediately. 332 */ 333 int 334 smb_net_txr_send(struct sonode *so, smb_txlst_t *txl, smb_txreq_t *txr) 335 { 336 list_t local; 337 int rc = 0; 338 iovec_t iov; 339 struct msghdr msg; 340 struct uio uio; 341 342 ASSERT(txl->tl_magic == SMB_TXLST_MAGIC); 343 344 mutex_enter(&txl->tl_mutex); 345 list_insert_tail(&txl->tl_list, txr); 346 if (txl->tl_active) { 347 mutex_exit(&txl->tl_mutex); 348 return (0); 349 } 350 txl->tl_active = B_TRUE; 351 352 list_create(&local, sizeof (smb_txreq_t), 353 offsetof(smb_txreq_t, tr_lnd)); 354 355 while (!list_is_empty(&txl->tl_list)) { 356 list_move_tail(&local, &txl->tl_list); 357 mutex_exit(&txl->tl_mutex); 358 while ((txr = list_head(&local)) != NULL) { 359 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC); 360 list_remove(&local, txr); 361 362 iov.iov_base = (void *)txr->tr_buf; 363 iov.iov_len = txr->tr_len; 364 365 bzero(&msg, sizeof (msg)); 366 msg.msg_iov = &iov; 367 msg.msg_flags = MSG_WAITALL; 368 msg.msg_iovlen = 1; 369 370 bzero(&uio, sizeof (uio)); 371 uio.uio_iov = &iov; 372 uio.uio_iovcnt = 1; 373 uio.uio_segflg = UIO_SYSSPACE; 374 uio.uio_resid = txr->tr_len; 375 376 rc = sosendmsg(so, &msg, &uio); 377 378 smb_net_txr_free(txr); 379 380 if ((rc == 0) && (uio.uio_resid == 0)) 381 continue; 382 383 if (rc == 0) 384 rc = -1; 385 386 while ((txr = list_head(&local)) != NULL) { 387 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC); 388 list_remove(&local, txr); 389 smb_net_txr_free(txr); 390 } 391 break; 392 } 393 mutex_enter(&txl->tl_mutex); 394 if (rc == 0) 395 continue; 396 397 while ((txr = list_head(&txl->tl_list)) != NULL) { 398 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC); 399 list_remove(&txl->tl_list, txr); 400 smb_net_txr_free(txr); 401 } 402 break; 403 } 404 txl->tl_active = B_FALSE; 405 mutex_exit(&txl->tl_mutex); 406 return (rc); 407 } 408