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