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