xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_net.c (revision ed5289f91b9bf164dccd6c75398362be77a4478d)
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