xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_net.c (revision 5970:b401e4f18a28)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
22*5970Sjb150015  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw 
275331Samw #pragma ident	"%Z%%M%	%I%	%E% SMI"
285331Samw 
295331Samw #include <sys/types.h>
305331Samw #include <sys/param.h>
315331Samw #include <sys/cpuvar.h>
325331Samw #include <sys/ddi.h>
335331Samw #include <sys/sunddi.h>
345331Samw #include <sys/time.h>
355331Samw #include <sys/varargs.h>
365331Samw #include <sys/modctl.h>
375331Samw #include <sys/pathname.h>
385331Samw #include <sys/fs/snode.h>
395331Samw #include <sys/fs/dv_node.h>
405331Samw #include <sys/vnode.h>
415331Samw #undef mem_free /* XXX Remove this after we convert everything to kmem_alloc */
425331Samw 
435331Samw #include <smbsrv/smb_vops.h>
445331Samw #include <smbsrv/smb.h>
455331Samw #include <smbsrv/mlsvc.h>
465331Samw #include <smbsrv/smbvar.h>
475331Samw #include <smbsrv/smb_kproto.h>
485331Samw 
495331Samw /*
505331Samw  * SMB Network Socket API
515331Samw  *
525331Samw  * smb_socreate:	Creates an socket based on domain/type.
535331Samw  * smb_soshutdown:	Disconnect a socket created with smb_socreate
545331Samw  * smb_sodestroy:	Release resources associated with a socket
555331Samw  * smb_sosend:		Send the contents of a buffer on a socket
565331Samw  * smb_sorecv:		Receive data into a buffer from a socket
575331Samw  * smb_iov_sosend:	Send the contents of an iovec on a socket
585331Samw  * smb_iov_sorecv:	Receive data into an iovec from a socket
595331Samw  */
605331Samw 
615331Samw struct sonode *
625331Samw smb_socreate(int domain, int type, int protocol)
635331Samw {
645331Samw 	vnode_t		*dvp		= NULL;
655331Samw 	vnode_t		*vp		= NULL;
665331Samw 	struct snode	*csp		= NULL;
675331Samw 	int		err		= 0;
685331Samw 	major_t		maj;
695331Samw 
705331Samw 	if ((vp = solookup(domain, type, protocol, NULL, &err)) == NULL) {
715331Samw 
725331Samw 		/*
735331Samw 		 * solookup calls sogetvp if the vp is not found in the cache.
745331Samw 		 * Since the call to sogetvp is hardwired to use USERSPACE
755331Samw 		 * and declared static we'll do the work here instead.
765331Samw 		 */
775331Samw 		err = lookupname(type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp",
785331Samw 		    UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
795331Samw 		if (err)
805331Samw 			return (NULL);
815331Samw 
825331Samw 		/* Check that it is the correct vnode */
835331Samw 		if (vp->v_type != VCHR) {
845331Samw 			VN_RELE(vp);
855331Samw 			return (NULL);
865331Samw 		}
875331Samw 
885331Samw 		csp = VTOS(VTOS(vp)->s_commonvp);
895331Samw 		if (!(csp->s_flag & SDIPSET)) {
905331Samw 			char    *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
915331Samw 			err = ddi_dev_pathname(vp->v_rdev, S_IFCHR,
925331Samw 			    pathname);
935331Samw 			if (err == 0) {
945331Samw 				err = devfs_lookupname(pathname, NULLVPP,
955331Samw 				    &dvp);
965331Samw 			}
975331Samw 			VN_RELE(vp);
985331Samw 			kmem_free(pathname, MAXPATHLEN);
995331Samw 			if (err != 0) {
1005331Samw 				return (NULL);
1015331Samw 			}
1025331Samw 			vp = dvp;
1035331Samw 		}
1045331Samw 
1055331Samw 		maj = getmajor(vp->v_rdev);
1065331Samw 		if (!STREAMSTAB(maj)) {
1075331Samw 			VN_RELE(vp);
1085331Samw 			return (NULL);
1095331Samw 		}
1105331Samw 	}
1115331Samw 
1125331Samw 	return (socreate(vp, domain, type, protocol, SOV_DEFAULT, NULL, &err));
1135331Samw }
1145331Samw 
1155331Samw /*
1165331Samw  * smb_soshutdown will disconnect the socket and prevent subsequent PDU
1175331Samw  * reception and transmission.  The sonode still exists but its state
1185331Samw  * gets modified to indicate it is no longer connected.  Calls to
1195331Samw  * smb_sorecv/smb_iov_sorecv will return so smb_soshutdown can be used
1205331Samw  * regain control of a thread stuck in smb_sorecv.
1215331Samw  */
1225331Samw void
1235331Samw smb_soshutdown(struct sonode *so)
1245331Samw {
1255331Samw 	(void) soshutdown(so, SHUT_RDWR);
1265331Samw }
1275331Samw 
1285331Samw /*
1295331Samw  * smb_sodestroy releases all resources associated with a socket previously
1305331Samw  * created with smb_socreate.  The socket must be shutdown using smb_soshutdown
1315331Samw  * before the socket is destroyed with smb_sodestroy, otherwise undefined
1325331Samw  * behavior will result.
1335331Samw  */
1345331Samw void
1355331Samw smb_sodestroy(struct sonode *so)
1365331Samw {
1375331Samw 	vnode_t *vp = SOTOV(so);
1385331Samw 
1395331Samw 	(void) VOP_CLOSE(vp, 0, 1, 0, kcred, NULL);
1405331Samw 	VN_RELE(vp);
1415331Samw }
1425331Samw 
1435331Samw int
1445331Samw smb_sorecv(struct sonode *so, void *msg, size_t len)
1455331Samw {
1465331Samw 	iovec_t iov;
1475331Samw 	int err;
1485331Samw 
1495331Samw 	ASSERT(so != NULL);
1505331Samw 	ASSERT(len != 0);
1515331Samw 
1525331Samw 	/*
1535331Samw 	 * Fill in iovec and receive data
1545331Samw 	 */
1555331Samw 	iov.iov_base = msg;
1565331Samw 	iov.iov_len = len;
1575331Samw 
1585331Samw 	if ((err = smb_iov_sorecv(so, &iov, 1, len)) != 0) {
1595331Samw 		return (err);
1605331Samw 	}
1615331Samw 
1625331Samw 	/* Successful receive */
1635331Samw 	return (0);
1645331Samw }
1655331Samw 
1665331Samw /*
1675331Samw  * smb_iov_sorecv - Receives an iovec from a connection
1685331Samw  *
1695331Samw  * This function gets the data asked for from the socket.  It will return
1705331Samw  * only when all the requested data has been retrieved or if an error
1715331Samw  * occurs.
1725331Samw  *
1735331Samw  * Returns 0 for success, the socket errno value if sorecvmsg fails, and
1745331Samw  * -1 if sorecvmsg returns success but uio_resid != 0
1755331Samw  */
1765331Samw int
1775331Samw smb_iov_sorecv(struct sonode *so, iovec_t *iop, int iovlen, size_t total_len)
1785331Samw {
1795331Samw 	struct msghdr		msg;
1805331Samw 	struct uio		uio;
1815331Samw 	int			error;
1825331Samw 
1835331Samw 	ASSERT(iop != NULL);
1845331Samw 
1855331Samw 	/* Initialization of the message header. */
1865331Samw 	bzero(&msg, sizeof (msg));
1875331Samw 	msg.msg_iov	= iop;
1885331Samw 	msg.msg_flags	= MSG_WAITALL;
1895331Samw 	msg.msg_iovlen	= iovlen;
1905331Samw 
1915331Samw 	/* Initialization of the uio structure. */
1925331Samw 	bzero(&uio, sizeof (uio));
1935331Samw 	uio.uio_iov	= iop;
1945331Samw 	uio.uio_iovcnt	= iovlen;
1955331Samw 	uio.uio_segflg	= UIO_SYSSPACE;
1965331Samw 	uio.uio_resid	= total_len;
1975331Samw 
1985331Samw 	if ((error = sorecvmsg(so, &msg, &uio)) == 0) {
1995331Samw 		/* Received data */
2005331Samw 		if (uio.uio_resid == 0) {
2015331Samw 			/* All requested data received.  Success */
2025331Samw 			return (0);
2035331Samw 		} else {
2045331Samw 			/* Not all data was sent.  Failure */
2055331Samw 			return (-1);
2065331Samw 		}
2075331Samw 	}
2085331Samw 
2095331Samw 	/* Receive failed */
2105331Samw 	return (error);
2115331Samw }
212*5970Sjb150015 
213*5970Sjb150015 /*
214*5970Sjb150015  * smb_net_txl_constructor
215*5970Sjb150015  *
216*5970Sjb150015  *	Transmit list constructor
217*5970Sjb150015  */
218*5970Sjb150015 void
219*5970Sjb150015 smb_net_txl_constructor(smb_txlst_t *txl)
220*5970Sjb150015 {
221*5970Sjb150015 	ASSERT(txl->tl_magic != SMB_TXLST_MAGIC);
222*5970Sjb150015 
223*5970Sjb150015 	mutex_init(&txl->tl_mutex, NULL, MUTEX_DEFAULT, NULL);
224*5970Sjb150015 	list_create(&txl->tl_list, sizeof (smb_txbuf_t),
225*5970Sjb150015 	    offsetof(smb_txbuf_t, tb_lnd));
226*5970Sjb150015 	txl->tl_active = B_FALSE;
227*5970Sjb150015 	txl->tl_magic = SMB_TXLST_MAGIC;
228*5970Sjb150015 }
229*5970Sjb150015 
230*5970Sjb150015 /*
231*5970Sjb150015  * smb_net_txl_destructor
232*5970Sjb150015  *
233*5970Sjb150015  *	Transmit list destructor
234*5970Sjb150015  */
235*5970Sjb150015 void
236*5970Sjb150015 smb_net_txl_destructor(smb_txlst_t *txl)
237*5970Sjb150015 {
238*5970Sjb150015 	ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
239*5970Sjb150015 
240*5970Sjb150015 	txl->tl_magic = 0;
241*5970Sjb150015 	list_destroy(&txl->tl_list);
242*5970Sjb150015 	mutex_destroy(&txl->tl_mutex);
243*5970Sjb150015 }
244*5970Sjb150015 
245*5970Sjb150015 /*
246*5970Sjb150015  * smb_net_txb_alloc
247*5970Sjb150015  *
248*5970Sjb150015  *	Transmit buffer allocator
249*5970Sjb150015  */
250*5970Sjb150015 smb_txbuf_t *
251*5970Sjb150015 smb_net_txb_alloc(void)
252*5970Sjb150015 {
253*5970Sjb150015 	smb_txbuf_t	*txb;
254*5970Sjb150015 
255*5970Sjb150015 	txb = kmem_alloc(sizeof (smb_txbuf_t), KM_SLEEP);
256*5970Sjb150015 
257*5970Sjb150015 	bzero(&txb->tb_lnd, sizeof (txb->tb_lnd));
258*5970Sjb150015 	txb->tb_len = 0;
259*5970Sjb150015 	txb->tb_magic = SMB_TXBUF_MAGIC;
260*5970Sjb150015 
261*5970Sjb150015 	return (txb);
262*5970Sjb150015 }
263*5970Sjb150015 
264*5970Sjb150015 /*
265*5970Sjb150015  * smb_net_txb_free
266*5970Sjb150015  *
267*5970Sjb150015  *	Transmit buffer deallocator
268*5970Sjb150015  */
269*5970Sjb150015 void
270*5970Sjb150015 smb_net_txb_free(smb_txbuf_t *txb)
271*5970Sjb150015 {
272*5970Sjb150015 	ASSERT(txb->tb_magic == SMB_TXBUF_MAGIC);
273*5970Sjb150015 	ASSERT(!list_link_active(&txb->tb_lnd));
274*5970Sjb150015 
275*5970Sjb150015 	txb->tb_magic = 0;
276*5970Sjb150015 	kmem_free(txb, sizeof (smb_txbuf_t));
277*5970Sjb150015 }
278*5970Sjb150015 
279*5970Sjb150015 /*
280*5970Sjb150015  * smb_net_txb_send
281*5970Sjb150015  *
282*5970Sjb150015  *	This routine puts the transmit buffer passed in on the wire. If another
283*5970Sjb150015  *	thread is already draining the transmit list, the transmit buffer is
284*5970Sjb150015  *	queued and the routine returns immediately.
285*5970Sjb150015  */
286*5970Sjb150015 int
287*5970Sjb150015 smb_net_txb_send(struct sonode *so, smb_txlst_t *txl, smb_txbuf_t *txb)
288*5970Sjb150015 {
289*5970Sjb150015 	list_t		local;
290*5970Sjb150015 	int		rc = 0;
291*5970Sjb150015 	iovec_t		iov;
292*5970Sjb150015 	struct msghdr	msg;
293*5970Sjb150015 	struct uio	uio;
294*5970Sjb150015 
295*5970Sjb150015 	ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
296*5970Sjb150015 
297*5970Sjb150015 	mutex_enter(&txl->tl_mutex);
298*5970Sjb150015 	list_insert_tail(&txl->tl_list, txb);
299*5970Sjb150015 	if (txl->tl_active) {
300*5970Sjb150015 		mutex_exit(&txl->tl_mutex);
301*5970Sjb150015 		return (0);
302*5970Sjb150015 	}
303*5970Sjb150015 
304*5970Sjb150015 	txl->tl_active = B_TRUE;
305*5970Sjb150015 	list_create(&local, sizeof (smb_txbuf_t),
306*5970Sjb150015 	    offsetof(smb_txbuf_t, tb_lnd));
307*5970Sjb150015 
308*5970Sjb150015 	while (!list_is_empty(&txl->tl_list)) {
309*5970Sjb150015 		list_move_tail(&local, &txl->tl_list);
310*5970Sjb150015 		mutex_exit(&txl->tl_mutex);
311*5970Sjb150015 		while ((txb = list_head(&local)) != NULL) {
312*5970Sjb150015 			ASSERT(txb->tb_magic == SMB_TXBUF_MAGIC);
313*5970Sjb150015 			list_remove(&local, txb);
314*5970Sjb150015 
315*5970Sjb150015 			iov.iov_base = (void *)txb->tb_data;
316*5970Sjb150015 			iov.iov_len = txb->tb_len;
317*5970Sjb150015 
318*5970Sjb150015 			bzero(&msg, sizeof (msg));
319*5970Sjb150015 			msg.msg_iov	= &iov;
320*5970Sjb150015 			msg.msg_flags	= MSG_WAITALL;
321*5970Sjb150015 			msg.msg_iovlen	= 1;
322*5970Sjb150015 
323*5970Sjb150015 			bzero(&uio, sizeof (uio));
324*5970Sjb150015 			uio.uio_iov	= &iov;
325*5970Sjb150015 			uio.uio_iovcnt	= 1;
326*5970Sjb150015 			uio.uio_segflg	= UIO_SYSSPACE;
327*5970Sjb150015 			uio.uio_resid	= txb->tb_len;
328*5970Sjb150015 
329*5970Sjb150015 			rc = sosendmsg(so, &msg, &uio);
330*5970Sjb150015 
331*5970Sjb150015 			smb_net_txb_free(txb);
332*5970Sjb150015 
333*5970Sjb150015 			if ((rc == 0) && (uio.uio_resid == 0))
334*5970Sjb150015 					continue;
335*5970Sjb150015 
336*5970Sjb150015 			if (rc == 0)
337*5970Sjb150015 				rc = -1;
338*5970Sjb150015 
339*5970Sjb150015 			while ((txb = list_head(&local)) != NULL) {
340*5970Sjb150015 				ASSERT(txb->tb_magic == SMB_TXBUF_MAGIC);
341*5970Sjb150015 				list_remove(&local, txb);
342*5970Sjb150015 				smb_net_txb_free(txb);
343*5970Sjb150015 			}
344*5970Sjb150015 			break;
345*5970Sjb150015 		}
346*5970Sjb150015 		mutex_enter(&txl->tl_mutex);
347*5970Sjb150015 
348*5970Sjb150015 		if (rc == 0)
349*5970Sjb150015 			continue;
350*5970Sjb150015 
351*5970Sjb150015 		while ((txb = list_head(&txl->tl_list)) != NULL) {
352*5970Sjb150015 			ASSERT(txb->tb_magic == SMB_TXBUF_MAGIC);
353*5970Sjb150015 			list_remove(&txl->tl_list, txb);
354*5970Sjb150015 			smb_net_txb_free(txb);
355*5970Sjb150015 		}
356*5970Sjb150015 		break;
357*5970Sjb150015 	}
358*5970Sjb150015 	txl->tl_active = B_FALSE;
359*5970Sjb150015 	mutex_exit(&txl->tl_mutex);
360*5970Sjb150015 
361*5970Sjb150015 	return (rc);
362*5970Sjb150015 }
363