xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_net.c (revision 6496:76a38b87ee13)
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 /*
225970Sjb150015  * 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/smb_kproto.h>
47*6496Sjb150015 #include <smbsrv/smb_kstat.h>
48*6496Sjb150015 
49*6496Sjb150015 static	kmem_cache_t	*smb_txr_cache = NULL;
50*6496Sjb150015 
51*6496Sjb150015 /*
52*6496Sjb150015  * smb_net_init
53*6496Sjb150015  *
54*6496Sjb150015  *	This function initializes the resources necessary to access the
55*6496Sjb150015  *	network. It assumes it won't be called simultaneously by multiple
56*6496Sjb150015  *	threads.
57*6496Sjb150015  *
58*6496Sjb150015  * Return Value
59*6496Sjb150015  *
60*6496Sjb150015  *	0	Initialization successful
61*6496Sjb150015  *	ENOMEM	Initialization failed
62*6496Sjb150015  */
63*6496Sjb150015 int
64*6496Sjb150015 smb_net_init(void)
65*6496Sjb150015 {
66*6496Sjb150015 	int	rc = 0;
67*6496Sjb150015 
68*6496Sjb150015 	ASSERT(smb_txr_cache == NULL);
69*6496Sjb150015 
70*6496Sjb150015 	smb_txr_cache = kmem_cache_create(SMBSRV_KSTAT_TXRCACHE,
71*6496Sjb150015 	    sizeof (smb_txreq_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
72*6496Sjb150015 	if (smb_txr_cache == NULL)
73*6496Sjb150015 		rc = ENOMEM;
74*6496Sjb150015 	return (rc);
75*6496Sjb150015 }
76*6496Sjb150015 
77*6496Sjb150015 /*
78*6496Sjb150015  * smb_net_fini
79*6496Sjb150015  *
80*6496Sjb150015  *	This function releases the resources allocated by smb_net_init(). It
81*6496Sjb150015  *	assumes it won't be called simultaneously by multiple threads.
82*6496Sjb150015  *	This function can safely be called even if smb_net_init() hasn't been
83*6496Sjb150015  *	called previously.
84*6496Sjb150015  *
85*6496Sjb150015  * Return Value
86*6496Sjb150015  *
87*6496Sjb150015  *	None
88*6496Sjb150015  */
89*6496Sjb150015 void
90*6496Sjb150015 smb_net_fini(void)
91*6496Sjb150015 {
92*6496Sjb150015 	if (smb_txr_cache) {
93*6496Sjb150015 		kmem_cache_destroy(smb_txr_cache);
94*6496Sjb150015 		smb_txr_cache = NULL;
95*6496Sjb150015 	}
96*6496Sjb150015 }
975331Samw 
985331Samw /*
995331Samw  * SMB Network Socket API
1005331Samw  *
1015331Samw  * smb_socreate:	Creates an socket based on domain/type.
1025331Samw  * smb_soshutdown:	Disconnect a socket created with smb_socreate
1035331Samw  * smb_sodestroy:	Release resources associated with a socket
1045331Samw  * smb_sosend:		Send the contents of a buffer on a socket
1055331Samw  * smb_sorecv:		Receive data into a buffer from a socket
1065331Samw  * smb_iov_sosend:	Send the contents of an iovec on a socket
1075331Samw  * smb_iov_sorecv:	Receive data into an iovec from a socket
1085331Samw  */
1095331Samw 
1105331Samw struct sonode *
1115331Samw smb_socreate(int domain, int type, int protocol)
1125331Samw {
1135331Samw 	vnode_t		*dvp		= NULL;
1145331Samw 	vnode_t		*vp		= NULL;
1155331Samw 	struct snode	*csp		= NULL;
1165331Samw 	int		err		= 0;
1175331Samw 	major_t		maj;
1185331Samw 
1195331Samw 	if ((vp = solookup(domain, type, protocol, NULL, &err)) == NULL) {
1205331Samw 
1215331Samw 		/*
1225331Samw 		 * solookup calls sogetvp if the vp is not found in the cache.
1235331Samw 		 * Since the call to sogetvp is hardwired to use USERSPACE
1245331Samw 		 * and declared static we'll do the work here instead.
1255331Samw 		 */
1265331Samw 		err = lookupname(type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp",
1275331Samw 		    UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
1285331Samw 		if (err)
1295331Samw 			return (NULL);
1305331Samw 
1315331Samw 		/* Check that it is the correct vnode */
1325331Samw 		if (vp->v_type != VCHR) {
1335331Samw 			VN_RELE(vp);
1345331Samw 			return (NULL);
1355331Samw 		}
1365331Samw 
1375331Samw 		csp = VTOS(VTOS(vp)->s_commonvp);
1385331Samw 		if (!(csp->s_flag & SDIPSET)) {
1395331Samw 			char    *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1405331Samw 			err = ddi_dev_pathname(vp->v_rdev, S_IFCHR,
1415331Samw 			    pathname);
1425331Samw 			if (err == 0) {
1435331Samw 				err = devfs_lookupname(pathname, NULLVPP,
1445331Samw 				    &dvp);
1455331Samw 			}
1465331Samw 			VN_RELE(vp);
1475331Samw 			kmem_free(pathname, MAXPATHLEN);
1485331Samw 			if (err != 0) {
1495331Samw 				return (NULL);
1505331Samw 			}
1515331Samw 			vp = dvp;
1525331Samw 		}
1535331Samw 
1545331Samw 		maj = getmajor(vp->v_rdev);
1555331Samw 		if (!STREAMSTAB(maj)) {
1565331Samw 			VN_RELE(vp);
1575331Samw 			return (NULL);
1585331Samw 		}
1595331Samw 	}
1605331Samw 
1615331Samw 	return (socreate(vp, domain, type, protocol, SOV_DEFAULT, NULL, &err));
1625331Samw }
1635331Samw 
1645331Samw /*
1655331Samw  * smb_soshutdown will disconnect the socket and prevent subsequent PDU
1665331Samw  * reception and transmission.  The sonode still exists but its state
1675331Samw  * gets modified to indicate it is no longer connected.  Calls to
1685331Samw  * smb_sorecv/smb_iov_sorecv will return so smb_soshutdown can be used
1695331Samw  * regain control of a thread stuck in smb_sorecv.
1705331Samw  */
1715331Samw void
1725331Samw smb_soshutdown(struct sonode *so)
1735331Samw {
1745331Samw 	(void) soshutdown(so, SHUT_RDWR);
1755331Samw }
1765331Samw 
1775331Samw /*
1785331Samw  * smb_sodestroy releases all resources associated with a socket previously
1795331Samw  * created with smb_socreate.  The socket must be shutdown using smb_soshutdown
1805331Samw  * before the socket is destroyed with smb_sodestroy, otherwise undefined
1815331Samw  * behavior will result.
1825331Samw  */
1835331Samw void
1845331Samw smb_sodestroy(struct sonode *so)
1855331Samw {
1865331Samw 	vnode_t *vp = SOTOV(so);
1875331Samw 
1885331Samw 	(void) VOP_CLOSE(vp, 0, 1, 0, kcred, NULL);
1895331Samw 	VN_RELE(vp);
1905331Samw }
1915331Samw 
1925331Samw int
1935331Samw smb_sorecv(struct sonode *so, void *msg, size_t len)
1945331Samw {
1955331Samw 	iovec_t iov;
1965331Samw 	int err;
1975331Samw 
1985331Samw 	ASSERT(so != NULL);
1995331Samw 	ASSERT(len != 0);
2005331Samw 
2015331Samw 	/*
2025331Samw 	 * Fill in iovec and receive data
2035331Samw 	 */
2045331Samw 	iov.iov_base = msg;
2055331Samw 	iov.iov_len = len;
2065331Samw 
2075331Samw 	if ((err = smb_iov_sorecv(so, &iov, 1, len)) != 0) {
2085331Samw 		return (err);
2095331Samw 	}
2105331Samw 
2115331Samw 	/* Successful receive */
2125331Samw 	return (0);
2135331Samw }
2145331Samw 
2155331Samw /*
2165331Samw  * smb_iov_sorecv - Receives an iovec from a connection
2175331Samw  *
2185331Samw  * This function gets the data asked for from the socket.  It will return
2195331Samw  * only when all the requested data has been retrieved or if an error
2205331Samw  * occurs.
2215331Samw  *
2225331Samw  * Returns 0 for success, the socket errno value if sorecvmsg fails, and
2235331Samw  * -1 if sorecvmsg returns success but uio_resid != 0
2245331Samw  */
2255331Samw int
2265331Samw smb_iov_sorecv(struct sonode *so, iovec_t *iop, int iovlen, size_t total_len)
2275331Samw {
2285331Samw 	struct msghdr		msg;
2295331Samw 	struct uio		uio;
2305331Samw 	int			error;
2315331Samw 
2325331Samw 	ASSERT(iop != NULL);
2335331Samw 
2345331Samw 	/* Initialization of the message header. */
2355331Samw 	bzero(&msg, sizeof (msg));
2365331Samw 	msg.msg_iov	= iop;
2375331Samw 	msg.msg_flags	= MSG_WAITALL;
2385331Samw 	msg.msg_iovlen	= iovlen;
2395331Samw 
2405331Samw 	/* Initialization of the uio structure. */
2415331Samw 	bzero(&uio, sizeof (uio));
2425331Samw 	uio.uio_iov	= iop;
2435331Samw 	uio.uio_iovcnt	= iovlen;
2445331Samw 	uio.uio_segflg	= UIO_SYSSPACE;
2455331Samw 	uio.uio_resid	= total_len;
2465331Samw 
2475331Samw 	if ((error = sorecvmsg(so, &msg, &uio)) == 0) {
2485331Samw 		/* Received data */
2495331Samw 		if (uio.uio_resid == 0) {
2505331Samw 			/* All requested data received.  Success */
2515331Samw 			return (0);
2525331Samw 		} else {
2535331Samw 			/* Not all data was sent.  Failure */
2545331Samw 			return (-1);
2555331Samw 		}
2565331Samw 	}
2575331Samw 
2585331Samw 	/* Receive failed */
2595331Samw 	return (error);
2605331Samw }
2615970Sjb150015 
2625970Sjb150015 /*
2635970Sjb150015  * smb_net_txl_constructor
2645970Sjb150015  *
2655970Sjb150015  *	Transmit list constructor
2665970Sjb150015  */
2675970Sjb150015 void
2685970Sjb150015 smb_net_txl_constructor(smb_txlst_t *txl)
2695970Sjb150015 {
2705970Sjb150015 	ASSERT(txl->tl_magic != SMB_TXLST_MAGIC);
2715970Sjb150015 
2725970Sjb150015 	mutex_init(&txl->tl_mutex, NULL, MUTEX_DEFAULT, NULL);
273*6496Sjb150015 	list_create(&txl->tl_list, sizeof (smb_txreq_t),
274*6496Sjb150015 	    offsetof(smb_txreq_t, tr_lnd));
2755970Sjb150015 	txl->tl_active = B_FALSE;
2765970Sjb150015 	txl->tl_magic = SMB_TXLST_MAGIC;
2775970Sjb150015 }
2785970Sjb150015 
2795970Sjb150015 /*
2805970Sjb150015  * smb_net_txl_destructor
2815970Sjb150015  *
2825970Sjb150015  *	Transmit list destructor
2835970Sjb150015  */
2845970Sjb150015 void
2855970Sjb150015 smb_net_txl_destructor(smb_txlst_t *txl)
2865970Sjb150015 {
2875970Sjb150015 	ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
2885970Sjb150015 
2895970Sjb150015 	txl->tl_magic = 0;
2905970Sjb150015 	list_destroy(&txl->tl_list);
2915970Sjb150015 	mutex_destroy(&txl->tl_mutex);
2925970Sjb150015 }
2935970Sjb150015 
2945970Sjb150015 /*
295*6496Sjb150015  * smb_net_txr_alloc
2965970Sjb150015  *
2975970Sjb150015  *	Transmit buffer allocator
2985970Sjb150015  */
299*6496Sjb150015 smb_txreq_t *
300*6496Sjb150015 smb_net_txr_alloc(void)
3015970Sjb150015 {
302*6496Sjb150015 	smb_txreq_t	*txr;
3035970Sjb150015 
304*6496Sjb150015 	txr = kmem_cache_alloc(smb_txr_cache, KM_SLEEP);
305*6496Sjb150015 	txr->tr_len = 0;
306*6496Sjb150015 	bzero(&txr->tr_lnd, sizeof (txr->tr_lnd));
307*6496Sjb150015 	txr->tr_magic = SMB_TXREQ_MAGIC;
308*6496Sjb150015 	return (txr);
3095970Sjb150015 }
3105970Sjb150015 
3115970Sjb150015 /*
312*6496Sjb150015  * smb_net_txr_free
3135970Sjb150015  *
3145970Sjb150015  *	Transmit buffer deallocator
3155970Sjb150015  */
3165970Sjb150015 void
317*6496Sjb150015 smb_net_txr_free(smb_txreq_t *txr)
3185970Sjb150015 {
319*6496Sjb150015 	ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
320*6496Sjb150015 	ASSERT(!list_link_active(&txr->tr_lnd));
3215970Sjb150015 
322*6496Sjb150015 	txr->tr_magic = 0;
323*6496Sjb150015 	kmem_cache_free(smb_txr_cache, txr);
3245970Sjb150015 }
3255970Sjb150015 
3265970Sjb150015 /*
327*6496Sjb150015  * smb_net_txr_send
3285970Sjb150015  *
3295970Sjb150015  *	This routine puts the transmit buffer passed in on the wire. If another
3305970Sjb150015  *	thread is already draining the transmit list, the transmit buffer is
3315970Sjb150015  *	queued and the routine returns immediately.
3325970Sjb150015  */
3335970Sjb150015 int
334*6496Sjb150015 smb_net_txr_send(struct sonode *so, smb_txlst_t *txl, smb_txreq_t *txr)
3355970Sjb150015 {
3365970Sjb150015 	list_t		local;
3375970Sjb150015 	int		rc = 0;
3385970Sjb150015 	iovec_t		iov;
3395970Sjb150015 	struct msghdr	msg;
3405970Sjb150015 	struct uio	uio;
3415970Sjb150015 
3425970Sjb150015 	ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
3435970Sjb150015 
3445970Sjb150015 	mutex_enter(&txl->tl_mutex);
345*6496Sjb150015 	list_insert_tail(&txl->tl_list, txr);
3465970Sjb150015 	if (txl->tl_active) {
3475970Sjb150015 		mutex_exit(&txl->tl_mutex);
3485970Sjb150015 		return (0);
3495970Sjb150015 	}
350*6496Sjb150015 	txl->tl_active = B_TRUE;
3515970Sjb150015 
352*6496Sjb150015 	list_create(&local, sizeof (smb_txreq_t),
353*6496Sjb150015 	    offsetof(smb_txreq_t, tr_lnd));
3545970Sjb150015 
3555970Sjb150015 	while (!list_is_empty(&txl->tl_list)) {
3565970Sjb150015 		list_move_tail(&local, &txl->tl_list);
3575970Sjb150015 		mutex_exit(&txl->tl_mutex);
358*6496Sjb150015 		while ((txr = list_head(&local)) != NULL) {
359*6496Sjb150015 			ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
360*6496Sjb150015 			list_remove(&local, txr);
3615970Sjb150015 
362*6496Sjb150015 			iov.iov_base = (void *)txr->tr_buf;
363*6496Sjb150015 			iov.iov_len = txr->tr_len;
3645970Sjb150015 
3655970Sjb150015 			bzero(&msg, sizeof (msg));
3665970Sjb150015 			msg.msg_iov	= &iov;
3675970Sjb150015 			msg.msg_flags	= MSG_WAITALL;
3685970Sjb150015 			msg.msg_iovlen	= 1;
3695970Sjb150015 
3705970Sjb150015 			bzero(&uio, sizeof (uio));
3715970Sjb150015 			uio.uio_iov	= &iov;
3725970Sjb150015 			uio.uio_iovcnt	= 1;
3735970Sjb150015 			uio.uio_segflg	= UIO_SYSSPACE;
374*6496Sjb150015 			uio.uio_resid	= txr->tr_len;
3755970Sjb150015 
3765970Sjb150015 			rc = sosendmsg(so, &msg, &uio);
3775970Sjb150015 
378*6496Sjb150015 			smb_net_txr_free(txr);
3795970Sjb150015 
3805970Sjb150015 			if ((rc == 0) && (uio.uio_resid == 0))
381*6496Sjb150015 				continue;
3825970Sjb150015 
3835970Sjb150015 			if (rc == 0)
3845970Sjb150015 				rc = -1;
3855970Sjb150015 
386*6496Sjb150015 			while ((txr = list_head(&local)) != NULL) {
387*6496Sjb150015 				ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
388*6496Sjb150015 				list_remove(&local, txr);
389*6496Sjb150015 				smb_net_txr_free(txr);
3905970Sjb150015 			}
3915970Sjb150015 			break;
3925970Sjb150015 		}
3935970Sjb150015 		mutex_enter(&txl->tl_mutex);
3945970Sjb150015 		if (rc == 0)
3955970Sjb150015 			continue;
3965970Sjb150015 
397*6496Sjb150015 		while ((txr = list_head(&txl->tl_list)) != NULL) {
398*6496Sjb150015 			ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
399*6496Sjb150015 			list_remove(&txl->tl_list, txr);
400*6496Sjb150015 			smb_net_txr_free(txr);
4015970Sjb150015 		}
4025970Sjb150015 		break;
4035970Sjb150015 	}
4045970Sjb150015 	txl->tl_active = B_FALSE;
4055970Sjb150015 	mutex_exit(&txl->tl_mutex);
4065970Sjb150015 	return (rc);
4075970Sjb150015 }
408