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