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 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
26 */
27
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/time.h>
33 #include <sys/varargs.h>
34 #include <sys/modctl.h>
35 #include <sys/pathname.h>
36 #include <sys/vnode.h>
37 #include <sys/socket.h>
38 #include <sys/ksocket.h>
39 #undef mem_free /* XXX Remove this after we convert everything to kmem_alloc */
40
41 #include <smbsrv/smb_vops.h>
42 #include <smbsrv/smb.h>
43 #include <smbsrv/smb_kproto.h>
44 #include <smbsrv/smb_kstat.h>
45
46 static kmem_cache_t *smb_txr_cache = NULL;
47
48 /*
49 * smb_net_init
50 *
51 * This function initializes the resources necessary to access the
52 * network. It assumes it won't be called simultaneously by multiple
53 * threads.
54 *
55 * Return Value
56 *
57 * 0 Initialization successful
58 * ENOMEM Initialization failed
59 */
60 void
smb_net_init(void)61 smb_net_init(void)
62 {
63
64 if (smb_txr_cache != NULL)
65 return;
66
67 smb_txr_cache = kmem_cache_create(SMBSRV_KSTAT_TXRCACHE,
68 sizeof (smb_txreq_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
69 }
70
71 /*
72 * smb_net_fini
73 *
74 * This function releases the resources allocated by smb_net_init(). It
75 * assumes it won't be called simultaneously by multiple threads.
76 * This function can safely be called even if smb_net_init() hasn't been
77 * called previously.
78 *
79 * Return Value
80 *
81 * None
82 */
83 void
smb_net_fini(void)84 smb_net_fini(void)
85 {
86 if (smb_txr_cache) {
87 kmem_cache_destroy(smb_txr_cache);
88 smb_txr_cache = NULL;
89 }
90 }
91
92 /*
93 * SMB Network Socket API
94 *
95 * smb_socreate: Creates an socket based on domain/type.
96 * smb_soshutdown: Disconnect a socket created with smb_socreate
97 * smb_sodestroy: Release resources associated with a socket
98 * smb_sosend: Send the contents of a buffer on a socket
99 * smb_sorecv: Receive data into a buffer from a socket
100 * smb_iov_sosend: Send the contents of an iovec on a socket
101 * smb_iov_sorecv: Receive data into an iovec from a socket
102 */
103
104 ksocket_t
smb_socreate(int domain,int type,int protocol)105 smb_socreate(int domain, int type, int protocol)
106 {
107 ksocket_t sock;
108 int err = 0;
109
110 err = ksocket_socket(&sock, domain, type, protocol, KSOCKET_SLEEP,
111 CRED());
112
113 if (err != 0)
114 return (NULL);
115 else
116 return (sock);
117 }
118
119 /*
120 * smb_soshutdown will disconnect the socket and prevent subsequent PDU
121 * reception and transmission. The sonode still exists but its state
122 * gets modified to indicate it is no longer connected. Calls to
123 * smb_sorecv/smb_iov_sorecv will return so smb_soshutdown can be used
124 * regain control of a thread stuck in smb_sorecv.
125 */
126 void
smb_soshutdown(ksocket_t so)127 smb_soshutdown(ksocket_t so)
128 {
129 (void) ksocket_shutdown(so, SHUT_RDWR, CRED());
130 }
131
132 /*
133 * smb_sodestroy releases all resources associated with a socket previously
134 * created with smb_socreate. The socket must be shutdown using smb_soshutdown
135 * before the socket is destroyed with smb_sodestroy, otherwise undefined
136 * behavior will result.
137 */
138 void
smb_sodestroy(ksocket_t so)139 smb_sodestroy(ksocket_t so)
140 {
141 (void) ksocket_close(so, CRED());
142 }
143
144 int
smb_sorecv(ksocket_t so,void * msg,size_t len)145 smb_sorecv(ksocket_t so, void *msg, size_t len)
146 {
147 size_t recvd;
148 int err;
149
150 ASSERT(so != NULL);
151 ASSERT(len != 0);
152
153 if ((err = ksocket_recv(so, msg, len, MSG_WAITALL, &recvd,
154 CRED())) != 0) {
155 return (err);
156 }
157
158 /* Successful receive */
159 return ((recvd == len) ? 0 : -1);
160 }
161
162 /*
163 * smb_net_txl_constructor
164 *
165 * Transmit list constructor
166 */
167 void
smb_net_txl_constructor(smb_txlst_t * txl)168 smb_net_txl_constructor(smb_txlst_t *txl)
169 {
170 ASSERT(txl->tl_magic != SMB_TXLST_MAGIC);
171
172 mutex_init(&txl->tl_mutex, NULL, MUTEX_DEFAULT, NULL);
173 list_create(&txl->tl_list, sizeof (smb_txreq_t),
174 offsetof(smb_txreq_t, tr_lnd));
175 txl->tl_active = B_FALSE;
176 txl->tl_magic = SMB_TXLST_MAGIC;
177 }
178
179 /*
180 * smb_net_txl_destructor
181 *
182 * Transmit list destructor
183 */
184 void
smb_net_txl_destructor(smb_txlst_t * txl)185 smb_net_txl_destructor(smb_txlst_t *txl)
186 {
187 ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
188
189 txl->tl_magic = 0;
190 list_destroy(&txl->tl_list);
191 mutex_destroy(&txl->tl_mutex);
192 }
193
194 /*
195 * smb_net_txr_alloc
196 *
197 * Transmit buffer allocator
198 */
199 smb_txreq_t *
smb_net_txr_alloc(void)200 smb_net_txr_alloc(void)
201 {
202 smb_txreq_t *txr;
203
204 txr = kmem_cache_alloc(smb_txr_cache, KM_SLEEP);
205 txr->tr_len = 0;
206 bzero(&txr->tr_lnd, sizeof (txr->tr_lnd));
207 txr->tr_magic = SMB_TXREQ_MAGIC;
208 return (txr);
209 }
210
211 /*
212 * smb_net_txr_free
213 *
214 * Transmit buffer deallocator
215 */
216 void
smb_net_txr_free(smb_txreq_t * txr)217 smb_net_txr_free(smb_txreq_t *txr)
218 {
219 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
220 ASSERT(!list_link_active(&txr->tr_lnd));
221
222 txr->tr_magic = 0;
223 kmem_cache_free(smb_txr_cache, txr);
224 }
225
226 /*
227 * smb_net_txr_send
228 *
229 * This routine puts the transmit buffer passed in on the wire. If another
230 * thread is already draining the transmit list, the transmit buffer is
231 * queued and the routine returns immediately.
232 */
233 int
smb_net_txr_send(ksocket_t so,smb_txlst_t * txl,smb_txreq_t * txr)234 smb_net_txr_send(ksocket_t so, smb_txlst_t *txl, smb_txreq_t *txr)
235 {
236 list_t local;
237 int rc = 0;
238 size_t sent = 0;
239 size_t len;
240
241 ASSERT(txl->tl_magic == SMB_TXLST_MAGIC);
242
243 mutex_enter(&txl->tl_mutex);
244 list_insert_tail(&txl->tl_list, txr);
245 if (txl->tl_active) {
246 mutex_exit(&txl->tl_mutex);
247 return (0);
248 }
249 txl->tl_active = B_TRUE;
250
251 list_create(&local, sizeof (smb_txreq_t),
252 offsetof(smb_txreq_t, tr_lnd));
253
254 while (!list_is_empty(&txl->tl_list)) {
255 list_move_tail(&local, &txl->tl_list);
256 mutex_exit(&txl->tl_mutex);
257 while ((txr = list_head(&local)) != NULL) {
258 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
259 list_remove(&local, txr);
260
261 len = txr->tr_len;
262 rc = ksocket_send(so, txr->tr_buf, txr->tr_len,
263 MSG_WAITALL, &sent, CRED());
264 smb_net_txr_free(txr);
265 if ((rc == 0) && (sent == len))
266 continue;
267
268 if (rc == 0)
269 rc = -1;
270
271 while ((txr = list_head(&local)) != NULL) {
272 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
273 list_remove(&local, txr);
274 smb_net_txr_free(txr);
275 }
276 break;
277 }
278 mutex_enter(&txl->tl_mutex);
279 if (rc == 0)
280 continue;
281
282 while ((txr = list_head(&txl->tl_list)) != NULL) {
283 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC);
284 list_remove(&txl->tl_list, txr);
285 smb_net_txr_free(txr);
286 }
287 break;
288 }
289 txl->tl_active = B_FALSE;
290 mutex_exit(&txl->tl_mutex);
291 return (rc);
292 }
293