xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c (revision ac2bf314e776a9dcb4faf4d3f88408ed942f0fae)
1a90cf9f2SGordon Ross /*
2a90cf9f2SGordon Ross  * This file and its contents are supplied under the terms of the
3a90cf9f2SGordon Ross  * Common Development and Distribution License ("CDDL"), version 1.0.
4a90cf9f2SGordon Ross  * You may only use this file in accordance with the terms of version
5a90cf9f2SGordon Ross  * 1.0 of the CDDL.
6a90cf9f2SGordon Ross  *
7a90cf9f2SGordon Ross  * A full copy of the text of the CDDL should have accompanied this
8a90cf9f2SGordon Ross  * source.  A copy of the CDDL is also available via the Internet at
9a90cf9f2SGordon Ross  * http://www.illumos.org/license/CDDL.
10a90cf9f2SGordon Ross  */
11a90cf9f2SGordon Ross 
12a90cf9f2SGordon Ross /*
1358ccc3dcSGordon Ross  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
14a90cf9f2SGordon Ross  */
15a90cf9f2SGordon Ross 
16a90cf9f2SGordon Ross 
17a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h>
18a90cf9f2SGordon Ross #include <smbsrv/smb_kstat.h>
19a90cf9f2SGordon Ross #include <smbsrv/smb2.h>
20a90cf9f2SGordon Ross 
21a90cf9f2SGordon Ross /*
22a90cf9f2SGordon Ross  * Saved state for a command that "goes async".  When a compound request
23a90cf9f2SGordon Ross  * contains a command that may block indefinitely, the compound reply is
24a90cf9f2SGordon Ross  * composed with an "interim response" for that command, and information
25a90cf9f2SGordon Ross  * needed to actually dispatch that command is saved on a list of "async"
26a90cf9f2SGordon Ross  * commands for this compound request.  After the compound reply is sent,
27a90cf9f2SGordon Ross  * the list of async commands is processed, and those may block as long
28a90cf9f2SGordon Ross  * as they need to without affecting the initial compound request.
29a90cf9f2SGordon Ross  *
30a90cf9f2SGordon Ross  * Now interestingly, this "async" mechanism is not used with the full
31a90cf9f2SGordon Ross  * range of asynchrony that one might imagine.  The design of async
32a90cf9f2SGordon Ross  * request processing can be drastically simplified if we can assume
33a90cf9f2SGordon Ross  * that there's no need to run more than one async command at a time.
34a90cf9f2SGordon Ross  * With that simplifying assumption, we can continue using the current
35a90cf9f2SGordon Ross  * "one worker thread per request message" model, which has very simple
36a90cf9f2SGordon Ross  * locking rules etc.  The same worker thread that handles the initial
37a90cf9f2SGordon Ross  * compound request can handle the list of async requests.
38a90cf9f2SGordon Ross  *
39a90cf9f2SGordon Ross  * As it turns out, SMB2 clients do not try to use more than one "async"
40a90cf9f2SGordon Ross  * command in a compound.  If they were to do so, the [MS-SMB2] spec.
41a90cf9f2SGordon Ross  * allows us to decline additional async requests with an error.
42a90cf9f2SGordon Ross  *
43a90cf9f2SGordon Ross  * smb_async_req_t is the struct used to save an "async" request on
44a90cf9f2SGordon Ross  * the list of requests that had an interim reply in the initial
45a90cf9f2SGordon Ross  * compound reply.  This includes everything needed to restart
46a90cf9f2SGordon Ross  * processing at the async command.
47a90cf9f2SGordon Ross  */
48a90cf9f2SGordon Ross 
49a90cf9f2SGordon Ross typedef struct smb2_async_req {
50a90cf9f2SGordon Ross 
51a90cf9f2SGordon Ross 	smb_sdrc_t		(*ar_func)(smb_request_t *);
52a90cf9f2SGordon Ross 
53a90cf9f2SGordon Ross 	int ar_cmd_hdr;		/* smb2_cmd_hdr offset */
54a90cf9f2SGordon Ross 	int ar_cmd_len;		/* length from hdr */
55a90cf9f2SGordon Ross 
56a90cf9f2SGordon Ross 	/*
57a90cf9f2SGordon Ross 	 * SMB2 header fields.
58a90cf9f2SGordon Ross 	 */
59a90cf9f2SGordon Ross 	uint16_t		ar_cmd_code;
60a90cf9f2SGordon Ross 	uint16_t		ar_uid;
61a90cf9f2SGordon Ross 	uint16_t		ar_tid;
62a90cf9f2SGordon Ross 	uint32_t		ar_pid;
63a90cf9f2SGordon Ross 	uint32_t		ar_hdr_flags;
64a90cf9f2SGordon Ross 	uint64_t		ar_messageid;
65a90cf9f2SGordon Ross } smb2_async_req_t;
66a90cf9f2SGordon Ross 
67a90cf9f2SGordon Ross void smb2sr_do_async(smb_request_t *);
68a90cf9f2SGordon Ross smb_sdrc_t smb2_invalid_cmd(smb_request_t *);
69a90cf9f2SGordon Ross static void smb2_tq_work(void *);
70a90cf9f2SGordon Ross 
71adb064afSToomas Soome static const smb_disp_entry_t
72a90cf9f2SGordon Ross smb2_disp_table[SMB2__NCMDS] = {
73a90cf9f2SGordon Ross 
74a90cf9f2SGordon Ross 	/* text-name, pre, func, post, cmd-code, dialect, flags */
75a90cf9f2SGordon Ross 
76a90cf9f2SGordon Ross 	{  "smb2_negotiate", NULL,
77a90cf9f2SGordon Ross 	    smb2_negotiate, NULL, 0, 0,
78a90cf9f2SGordon Ross 	    SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
79a90cf9f2SGordon Ross 
80a90cf9f2SGordon Ross 	{  "smb2_session_setup", NULL,
81a90cf9f2SGordon Ross 	    smb2_session_setup, NULL, 0, 0,
82a90cf9f2SGordon Ross 	    SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
83a90cf9f2SGordon Ross 
84a90cf9f2SGordon Ross 	{  "smb2_logoff", NULL,
85a90cf9f2SGordon Ross 	    smb2_logoff, NULL, 0, 0,
86a90cf9f2SGordon Ross 	    SDDF_SUPPRESS_TID },
87a90cf9f2SGordon Ross 
88a90cf9f2SGordon Ross 	{  "smb2_tree_connect", NULL,
89a90cf9f2SGordon Ross 	    smb2_tree_connect, NULL, 0, 0,
90a90cf9f2SGordon Ross 	    SDDF_SUPPRESS_TID },
91a90cf9f2SGordon Ross 
92a90cf9f2SGordon Ross 	{  "smb2_tree_disconn", NULL,
93a90cf9f2SGordon Ross 	    smb2_tree_disconn, NULL, 0, 0 },
94a90cf9f2SGordon Ross 
95a90cf9f2SGordon Ross 	{  "smb2_create", NULL,
96a90cf9f2SGordon Ross 	    smb2_create, NULL, 0, 0 },
97a90cf9f2SGordon Ross 
98a90cf9f2SGordon Ross 	{  "smb2_close", NULL,
99a90cf9f2SGordon Ross 	    smb2_close, NULL, 0, 0 },
100a90cf9f2SGordon Ross 
101a90cf9f2SGordon Ross 	{  "smb2_flush", NULL,
102a90cf9f2SGordon Ross 	    smb2_flush, NULL, 0, 0 },
103a90cf9f2SGordon Ross 
104a90cf9f2SGordon Ross 	{  "smb2_read", NULL,
105a90cf9f2SGordon Ross 	    smb2_read, NULL, 0, 0 },
106a90cf9f2SGordon Ross 
107a90cf9f2SGordon Ross 	{  "smb2_write", NULL,
108a90cf9f2SGordon Ross 	    smb2_write, NULL, 0, 0 },
109a90cf9f2SGordon Ross 
110a90cf9f2SGordon Ross 	{  "smb2_lock", NULL,
111a90cf9f2SGordon Ross 	    smb2_lock, NULL, 0, 0 },
112a90cf9f2SGordon Ross 
113a90cf9f2SGordon Ross 	{  "smb2_ioctl", NULL,
114a90cf9f2SGordon Ross 	    smb2_ioctl, NULL, 0, 0 },
115a90cf9f2SGordon Ross 
116a90cf9f2SGordon Ross 	/*
117a90cf9f2SGordon Ross 	 * Note: Cancel gets the "invalid command" handler because
118a90cf9f2SGordon Ross 	 * that's always handled directly in the reader.  We should
119a90cf9f2SGordon Ross 	 * never get to the function using this table, but note:
120a90cf9f2SGordon Ross 	 * We CAN get here if a nasty client adds cancel to some
121a90cf9f2SGordon Ross 	 * compound message, which is a protocol violation.
122a90cf9f2SGordon Ross 	 */
123a90cf9f2SGordon Ross 	{  "smb2_cancel", NULL,
124a90cf9f2SGordon Ross 	    smb2_invalid_cmd, NULL, 0, 0 },
125a90cf9f2SGordon Ross 
126a90cf9f2SGordon Ross 	{  "smb2_echo", NULL,
127a90cf9f2SGordon Ross 	    smb2_echo, NULL, 0, 0,
128a90cf9f2SGordon Ross 	    SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
129a90cf9f2SGordon Ross 
130a90cf9f2SGordon Ross 	{  "smb2_query_dir", NULL,
131a90cf9f2SGordon Ross 	    smb2_query_dir, NULL, 0, 0 },
132a90cf9f2SGordon Ross 
133a90cf9f2SGordon Ross 	{  "smb2_change_notify", NULL,
134a90cf9f2SGordon Ross 	    smb2_change_notify, NULL, 0, 0 },
135a90cf9f2SGordon Ross 
136a90cf9f2SGordon Ross 	{  "smb2_query_info", NULL,
137a90cf9f2SGordon Ross 	    smb2_query_info, NULL, 0, 0 },
138a90cf9f2SGordon Ross 
139a90cf9f2SGordon Ross 	{  "smb2_set_info", NULL,
140a90cf9f2SGordon Ross 	    smb2_set_info, NULL, 0, 0 },
141a90cf9f2SGordon Ross 
142a90cf9f2SGordon Ross 	{  "smb2_oplock_break_ack", NULL,
143a90cf9f2SGordon Ross 	    smb2_oplock_break_ack, NULL, 0, 0 },
144a90cf9f2SGordon Ross 
145a90cf9f2SGordon Ross 	{  "smb2_invalid_cmd", NULL,
146a90cf9f2SGordon Ross 	    smb2_invalid_cmd, NULL, 0, 0,
147a90cf9f2SGordon Ross 	    SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
148a90cf9f2SGordon Ross };
149a90cf9f2SGordon Ross 
150a90cf9f2SGordon Ross smb_sdrc_t
151a90cf9f2SGordon Ross smb2_invalid_cmd(smb_request_t *sr)
152a90cf9f2SGordon Ross {
153a90cf9f2SGordon Ross #ifdef	DEBUG
154a90cf9f2SGordon Ross 	cmn_err(CE_NOTE, "clnt %s bad SMB2 cmd code",
155a90cf9f2SGordon Ross 	    sr->session->ip_addr_str);
156a90cf9f2SGordon Ross #endif
157a90cf9f2SGordon Ross 	sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
158a90cf9f2SGordon Ross 	return (SDRC_DROP_VC);
159a90cf9f2SGordon Ross }
160a90cf9f2SGordon Ross 
161a90cf9f2SGordon Ross /*
162a90cf9f2SGordon Ross  * This is the SMB2 handler for new smb requests, called from
163a90cf9f2SGordon Ross  * smb_session_reader after SMB negotiate is done.  For most SMB2
164a90cf9f2SGordon Ross  * requests, we just enqueue them for the smb_session_worker to
165a90cf9f2SGordon Ross  * execute via the task queue, so they can block for resources
166a90cf9f2SGordon Ross  * without stopping the reader thread.  A few protocol messages
167a90cf9f2SGordon Ross  * are special cases and are handled directly here in the reader
168a90cf9f2SGordon Ross  * thread so they don't wait for taskq scheduling.
169a90cf9f2SGordon Ross  *
170a90cf9f2SGordon Ross  * This function must either enqueue the new request for
171a90cf9f2SGordon Ross  * execution via the task queue, or execute it directly
172a90cf9f2SGordon Ross  * and then free it.  If this returns non-zero, the caller
173a90cf9f2SGordon Ross  * will drop the session.
174a90cf9f2SGordon Ross  */
175a90cf9f2SGordon Ross int
176a90cf9f2SGordon Ross smb2sr_newrq(smb_request_t *sr)
177a90cf9f2SGordon Ross {
178a90cf9f2SGordon Ross 	uint32_t magic;
179a90cf9f2SGordon Ross 	uint16_t command;
180a90cf9f2SGordon Ross 	int rc;
181a90cf9f2SGordon Ross 
182a90cf9f2SGordon Ross 	magic = LE_IN32(sr->sr_request_buf);
183a90cf9f2SGordon Ross 	if (magic != SMB2_PROTOCOL_MAGIC) {
184a90cf9f2SGordon Ross 		smb_request_free(sr);
185a90cf9f2SGordon Ross 		/* will drop the connection */
186a90cf9f2SGordon Ross 		return (EPROTO);
187a90cf9f2SGordon Ross 	}
188a90cf9f2SGordon Ross 
189a90cf9f2SGordon Ross 	/*
190a90cf9f2SGordon Ross 	 * Execute Cancel requests immediately, (here in the
191a90cf9f2SGordon Ross 	 * reader thread) so they won't wait for any other
192a90cf9f2SGordon Ross 	 * commands we might already have in the task queue.
193a90cf9f2SGordon Ross 	 * Cancel also skips signature verification and
194a90cf9f2SGordon Ross 	 * does not consume a sequence number.
195a90cf9f2SGordon Ross 	 * [MS-SMB2] 3.2.4.24 Cancellation...
196a90cf9f2SGordon Ross 	 */
197a90cf9f2SGordon Ross 	command = LE_IN16((uint8_t *)sr->sr_request_buf + 12);
198a90cf9f2SGordon Ross 	if (command == SMB2_CANCEL) {
199a90cf9f2SGordon Ross 		rc = smb2sr_newrq_cancel(sr);
200a90cf9f2SGordon Ross 		smb_request_free(sr);
201a90cf9f2SGordon Ross 		return (rc);
202a90cf9f2SGordon Ross 	}
203a90cf9f2SGordon Ross 
204a90cf9f2SGordon Ross 	/*
205a90cf9f2SGordon Ross 	 * Submit the request to the task queue, which calls
206a90cf9f2SGordon Ross 	 * smb2_tq_work when the workload permits.
207a90cf9f2SGordon Ross 	 */
208a90cf9f2SGordon Ross 	sr->sr_time_submitted = gethrtime();
209a90cf9f2SGordon Ross 	sr->sr_state = SMB_REQ_STATE_SUBMITTED;
210a90cf9f2SGordon Ross 	smb_srqueue_waitq_enter(sr->session->s_srqueue);
211a90cf9f2SGordon Ross 	(void) taskq_dispatch(sr->sr_server->sv_worker_pool,
212a90cf9f2SGordon Ross 	    smb2_tq_work, sr, TQ_SLEEP);
213a90cf9f2SGordon Ross 
214a90cf9f2SGordon Ross 	return (0);
215a90cf9f2SGordon Ross }
216a90cf9f2SGordon Ross 
217a90cf9f2SGordon Ross static void
218a90cf9f2SGordon Ross smb2_tq_work(void *arg)
219a90cf9f2SGordon Ross {
220a90cf9f2SGordon Ross 	smb_request_t	*sr;
221a90cf9f2SGordon Ross 	smb_srqueue_t	*srq;
222a90cf9f2SGordon Ross 
223a90cf9f2SGordon Ross 	sr = (smb_request_t *)arg;
224a90cf9f2SGordon Ross 	SMB_REQ_VALID(sr);
225a90cf9f2SGordon Ross 
226a90cf9f2SGordon Ross 	srq = sr->session->s_srqueue;
227a90cf9f2SGordon Ross 	smb_srqueue_waitq_to_runq(srq);
228a90cf9f2SGordon Ross 	sr->sr_worker = curthread;
229a90cf9f2SGordon Ross 	sr->sr_time_active = gethrtime();
230a90cf9f2SGordon Ross 
231a90cf9f2SGordon Ross 	/*
232a90cf9f2SGordon Ross 	 * In contrast with SMB1, SMB2 must _always_ dispatch to
233a90cf9f2SGordon Ross 	 * the handler function, because cancelled requests need
234a90cf9f2SGordon Ross 	 * an error reply (NT_STATUS_CANCELLED).
235a90cf9f2SGordon Ross 	 */
236a90cf9f2SGordon Ross 	smb2sr_work(sr);
237a90cf9f2SGordon Ross 
238a90cf9f2SGordon Ross 	smb_srqueue_runq_exit(srq);
239a90cf9f2SGordon Ross }
240a90cf9f2SGordon Ross 
241a90cf9f2SGordon Ross /*
242a90cf9f2SGordon Ross  * smb2sr_work
243a90cf9f2SGordon Ross  *
244a90cf9f2SGordon Ross  * This function processes each SMB command in the current request
245a90cf9f2SGordon Ross  * (which may be a compound request) building a reply containing
246a90cf9f2SGordon Ross  * SMB reply messages, one-to-one with the SMB commands.  Some SMB
247a90cf9f2SGordon Ross  * commands (change notify, blocking locks) may require both an
248a90cf9f2SGordon Ross  * "interim response" and a later "async response" at completion.
249a90cf9f2SGordon Ross  * In such cases, we'll encode the interim response in the reply
250a90cf9f2SGordon Ross  * compound we're building, and put the (now async) command on a
251a90cf9f2SGordon Ross  * list of commands that need further processing.  After we've
252a90cf9f2SGordon Ross  * finished processing the commands in this compound and building
253a90cf9f2SGordon Ross  * the compound reply, we'll send the compound reply, and finally
254a90cf9f2SGordon Ross  * process the list of async commands.
255a90cf9f2SGordon Ross  *
256a90cf9f2SGordon Ross  * As we work our way through the compound request and reply,
257a90cf9f2SGordon Ross  * we need to keep track of the bounds of the current request
258a90cf9f2SGordon Ross  * and reply.  For the request, this uses an MBC_SHADOW_CHAIN
259a90cf9f2SGordon Ross  * that begins at smb2_cmd_hdr.  The reply is appended to the
260a90cf9f2SGordon Ross  * sr->reply chain starting at smb2_reply_hdr.
261a90cf9f2SGordon Ross  *
262a90cf9f2SGordon Ross  * This function must always free the smb request.
263a90cf9f2SGordon Ross  */
264a90cf9f2SGordon Ross void
265a90cf9f2SGordon Ross smb2sr_work(struct smb_request *sr)
266a90cf9f2SGordon Ross {
267a90cf9f2SGordon Ross 	const smb_disp_entry_t	*sdd;
268a90cf9f2SGordon Ross 	smb_disp_stats_t	*sds;
269a90cf9f2SGordon Ross 	smb_session_t		*session;
270a90cf9f2SGordon Ross 	uint32_t		msg_len;
271a90cf9f2SGordon Ross 	uint16_t		cmd_idx;
272a90cf9f2SGordon Ross 	int			rc = 0;
273a90cf9f2SGordon Ross 	boolean_t		disconnect = B_FALSE;
274a90cf9f2SGordon Ross 	boolean_t		related;
275a90cf9f2SGordon Ross 
276a90cf9f2SGordon Ross 	session = sr->session;
277a90cf9f2SGordon Ross 
278a90cf9f2SGordon Ross 	ASSERT(sr->tid_tree == 0);
279a90cf9f2SGordon Ross 	ASSERT(sr->uid_user == 0);
280a90cf9f2SGordon Ross 	ASSERT(sr->fid_ofile == 0);
281a90cf9f2SGordon Ross 	sr->smb_fid = (uint16_t)-1;
282a90cf9f2SGordon Ross 	sr->smb2_status = 0;
283a90cf9f2SGordon Ross 
284a90cf9f2SGordon Ross 	/* temporary until we identify a user */
285a90cf9f2SGordon Ross 	sr->user_cr = zone_kcred();
286a90cf9f2SGordon Ross 
287a90cf9f2SGordon Ross 	mutex_enter(&sr->sr_mutex);
288a90cf9f2SGordon Ross 	switch (sr->sr_state) {
289a90cf9f2SGordon Ross 	case SMB_REQ_STATE_SUBMITTED:
290a90cf9f2SGordon Ross 	case SMB_REQ_STATE_CLEANED_UP:
291a90cf9f2SGordon Ross 		sr->sr_state = SMB_REQ_STATE_ACTIVE;
292a90cf9f2SGordon Ross 		break;
293a90cf9f2SGordon Ross 	default:
294a90cf9f2SGordon Ross 		ASSERT(0);
295a90cf9f2SGordon Ross 		/* FALLTHROUGH */
296a90cf9f2SGordon Ross 	case SMB_REQ_STATE_CANCELED:
297a90cf9f2SGordon Ross 		sr->smb2_status = NT_STATUS_CANCELLED;
298a90cf9f2SGordon Ross 		break;
299a90cf9f2SGordon Ross 	}
300a90cf9f2SGordon Ross 	mutex_exit(&sr->sr_mutex);
301a90cf9f2SGordon Ross 
302a90cf9f2SGordon Ross cmd_start:
303a90cf9f2SGordon Ross 	/*
304a90cf9f2SGordon Ross 	 * Decode the request header
305a90cf9f2SGordon Ross 	 *
306a90cf9f2SGordon Ross 	 * Most problems with decoding will result in the error
307a90cf9f2SGordon Ross 	 * STATUS_INVALID_PARAMETER.  If the decoding problem
308a90cf9f2SGordon Ross 	 * prevents continuing, we'll close the connection.
309a90cf9f2SGordon Ross 	 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
310a90cf9f2SGordon Ross 	 *
311a90cf9f2SGordon Ross 	 * We treat some status codes as if "sticky", meaning
312a90cf9f2SGordon Ross 	 * once they're set after some command handler returns,
313a90cf9f2SGordon Ross 	 * all remaining commands get this status without even
314a90cf9f2SGordon Ross 	 * calling the command-specific handler. The cancelled
315a90cf9f2SGordon Ross 	 * status is used above, and insufficient_resources is
316a90cf9f2SGordon Ross 	 * used when smb2sr_go_async declines to "go async".
317a90cf9f2SGordon Ross 	 * Otherwise initialize to zero (success).
318a90cf9f2SGordon Ross 	 */
319a90cf9f2SGordon Ross 	if (sr->smb2_status != NT_STATUS_CANCELLED &&
320a90cf9f2SGordon Ross 	    sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES)
321a90cf9f2SGordon Ross 		sr->smb2_status = 0;
322a90cf9f2SGordon Ross 
323a90cf9f2SGordon Ross 	sr->smb2_cmd_hdr = sr->command.chain_offset;
324a90cf9f2SGordon Ross 	if ((rc = smb2_decode_header(sr)) != 0) {
325a90cf9f2SGordon Ross 		cmn_err(CE_WARN, "clnt %s bad SMB2 header",
326a90cf9f2SGordon Ross 		    session->ip_addr_str);
327a90cf9f2SGordon Ross 		disconnect = B_TRUE;
328a90cf9f2SGordon Ross 		goto cleanup;
329a90cf9f2SGordon Ross 	}
330a90cf9f2SGordon Ross 
331a90cf9f2SGordon Ross 	/*
332a90cf9f2SGordon Ross 	 * The SMB2_FLAGS_SERVER_TO_REDIR should only appear
333a90cf9f2SGordon Ross 	 * in messages from the server back to the client.
334a90cf9f2SGordon Ross 	 */
335a90cf9f2SGordon Ross 	if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) {
336a90cf9f2SGordon Ross 		cmn_err(CE_WARN, "clnt %s bad SMB2 flags",
337a90cf9f2SGordon Ross 		    session->ip_addr_str);
338a90cf9f2SGordon Ross 		disconnect = B_TRUE;
339a90cf9f2SGordon Ross 		goto cleanup;
340a90cf9f2SGordon Ross 	}
341a90cf9f2SGordon Ross 	related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS);
342a90cf9f2SGordon Ross 
343a90cf9f2SGordon Ross 	/*
344a90cf9f2SGordon Ross 	 * In case we bail out with an error before we get to the
345a90cf9f2SGordon Ross 	 * section that computes the credit grant, initialize the
346a90cf9f2SGordon Ross 	 * response header fields so that credits won't change.
347a90cf9f2SGordon Ross 	 * Note: SMB 2.02 clients may send credit charge zero.
348a90cf9f2SGordon Ross 	 */
349a90cf9f2SGordon Ross 	if (sr->smb2_credit_charge == 0)
350a90cf9f2SGordon Ross 		sr->smb2_credit_charge = 1;
351a90cf9f2SGordon Ross 	sr->smb2_credit_response = sr->smb2_credit_charge;
352a90cf9f2SGordon Ross 
353a90cf9f2SGordon Ross 	/*
354a90cf9f2SGordon Ross 	 * Reserve space for the reply header, and save the offset.
355a90cf9f2SGordon Ross 	 * The reply header will be overwritten later.  If we have
356a90cf9f2SGordon Ross 	 * already exhausted the output space, then this client is
357a90cf9f2SGordon Ross 	 * trying something funny.  Log it and kill 'em.
358a90cf9f2SGordon Ross 	 */
359a90cf9f2SGordon Ross 	sr->smb2_reply_hdr = sr->reply.chain_offset;
360a90cf9f2SGordon Ross 	if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) {
361a90cf9f2SGordon Ross 		cmn_err(CE_WARN, "clnt %s excessive reply",
362a90cf9f2SGordon Ross 		    session->ip_addr_str);
363a90cf9f2SGordon Ross 		disconnect = B_TRUE;
364a90cf9f2SGordon Ross 		goto cleanup;
365a90cf9f2SGordon Ross 	}
366a90cf9f2SGordon Ross 
367a90cf9f2SGordon Ross 	/*
368a90cf9f2SGordon Ross 	 * Figure out the length of data following the SMB2 header.
369a90cf9f2SGordon Ross 	 * It ends at either the next SMB2 header if there is one
370a90cf9f2SGordon Ross 	 * (smb2_next_command != 0) or at the end of the message.
371a90cf9f2SGordon Ross 	 */
372a90cf9f2SGordon Ross 	if (sr->smb2_next_command != 0) {
373a90cf9f2SGordon Ross 		/* [MS-SMB2] says this is 8-byte aligned */
374a90cf9f2SGordon Ross 		msg_len = sr->smb2_next_command;
375a90cf9f2SGordon Ross 		if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) ||
376a90cf9f2SGordon Ross 		    ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) {
377a90cf9f2SGordon Ross 			cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd",
378a90cf9f2SGordon Ross 			    session->ip_addr_str);
379a90cf9f2SGordon Ross 			disconnect = B_TRUE;
380a90cf9f2SGordon Ross 			goto cleanup;
381a90cf9f2SGordon Ross 		}
382a90cf9f2SGordon Ross 	} else {
383a90cf9f2SGordon Ross 		msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr;
384a90cf9f2SGordon Ross 	}
385a90cf9f2SGordon Ross 
386a90cf9f2SGordon Ross 	/*
387a90cf9f2SGordon Ross 	 * Setup a shadow chain for this SMB2 command, starting
388a90cf9f2SGordon Ross 	 * with the header and ending at either the next command
389a90cf9f2SGordon Ross 	 * or the end of the message.  The signing check below
390a90cf9f2SGordon Ross 	 * needs the entire SMB2 command.  After that's done, we
391a90cf9f2SGordon Ross 	 * advance chain_offset to the end of the header where
392a90cf9f2SGordon Ross 	 * the command specific handlers continue decoding.
393a90cf9f2SGordon Ross 	 */
394a90cf9f2SGordon Ross 	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
395a90cf9f2SGordon Ross 	    sr->smb2_cmd_hdr, msg_len);
396a90cf9f2SGordon Ross 
397*ac2bf314SMatt Barden 	/*
398*ac2bf314SMatt Barden 	 * We will consume the data for this request from smb_data.
399*ac2bf314SMatt Barden 	 * That effectively consumes msg_len bytes from sr->command
400*ac2bf314SMatt Barden 	 * but doesn't update its chain_offset, so we need to update
401*ac2bf314SMatt Barden 	 * that here to make later received bytes accounting work.
402*ac2bf314SMatt Barden 	 */
403*ac2bf314SMatt Barden 	sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len;
404*ac2bf314SMatt Barden 	ASSERT(sr->command.chain_offset <= sr->command.max_bytes);
405*ac2bf314SMatt Barden 
406a90cf9f2SGordon Ross 	/*
407a90cf9f2SGordon Ross 	 * Validate the commmand code, get dispatch table entries.
408a90cf9f2SGordon Ross 	 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
409a90cf9f2SGordon Ross 	 *
410a90cf9f2SGordon Ross 	 * The last slot in the dispatch table is used to handle
411a90cf9f2SGordon Ross 	 * invalid commands.  Same for statistics.
412a90cf9f2SGordon Ross 	 */
413a90cf9f2SGordon Ross 	if (sr->smb2_cmd_code < SMB2_INVALID_CMD)
414a90cf9f2SGordon Ross 		cmd_idx = sr->smb2_cmd_code;
415a90cf9f2SGordon Ross 	else
416a90cf9f2SGordon Ross 		cmd_idx = SMB2_INVALID_CMD;
417a90cf9f2SGordon Ross 	sdd = &smb2_disp_table[cmd_idx];
418a90cf9f2SGordon Ross 	sds = &session->s_server->sv_disp_stats2[cmd_idx];
419a90cf9f2SGordon Ross 
420a90cf9f2SGordon Ross 	/*
421a90cf9f2SGordon Ross 	 * If this command is NOT "related" to the previous,
422a90cf9f2SGordon Ross 	 * clear out the UID, TID, FID state that might be
423a90cf9f2SGordon Ross 	 * left over from the previous command.
424a90cf9f2SGordon Ross 	 *
425a90cf9f2SGordon Ross 	 * If the command IS related, any new IDs are ignored,
426a90cf9f2SGordon Ross 	 * and we simply continue with the previous user, tree,
427a90cf9f2SGordon Ross 	 * and open file.
428a90cf9f2SGordon Ross 	 */
429a90cf9f2SGordon Ross 	if (!related) {
430a90cf9f2SGordon Ross 		/*
431a90cf9f2SGordon Ross 		 * Drop user, tree, file; carefully ordered to
432a90cf9f2SGordon Ross 		 * avoid dangling references: file, tree, user
433a90cf9f2SGordon Ross 		 */
434a90cf9f2SGordon Ross 		if (sr->fid_ofile != NULL) {
435a90cf9f2SGordon Ross 			smb_ofile_request_complete(sr->fid_ofile);
436a90cf9f2SGordon Ross 			smb_ofile_release(sr->fid_ofile);
437a90cf9f2SGordon Ross 			sr->fid_ofile = NULL;
438a90cf9f2SGordon Ross 		}
439a90cf9f2SGordon Ross 		if (sr->tid_tree != NULL) {
440a90cf9f2SGordon Ross 			smb_tree_release(sr->tid_tree);
441a90cf9f2SGordon Ross 			sr->tid_tree = NULL;
442a90cf9f2SGordon Ross 		}
443a90cf9f2SGordon Ross 		if (sr->uid_user != NULL) {
444a90cf9f2SGordon Ross 			smb_user_release(sr->uid_user);
445a90cf9f2SGordon Ross 			sr->uid_user = NULL;
446a90cf9f2SGordon Ross 			sr->user_cr = zone_kcred();
447a90cf9f2SGordon Ross 		}
448a90cf9f2SGordon Ross 	}
449a90cf9f2SGordon Ross 
450a90cf9f2SGordon Ross 	/*
451a90cf9f2SGordon Ross 	 * Make sure we have a user and tree as needed
452a90cf9f2SGordon Ross 	 * according to the flags for the this command.
453a90cf9f2SGordon Ross 	 * Note that we may have inherited these.
454a90cf9f2SGordon Ross 	 */
455a90cf9f2SGordon Ross 	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) {
456a90cf9f2SGordon Ross 		/*
457a90cf9f2SGordon Ross 		 * This command requires a user session.
458a90cf9f2SGordon Ross 		 */
459a90cf9f2SGordon Ross 		if (related) {
460a90cf9f2SGordon Ross 			/*
461a90cf9f2SGordon Ross 			 * Previous command should have given us a user.
462a90cf9f2SGordon Ross 			 * [MS-SMB2] 3.3.5.2 Handling Related Requests
463a90cf9f2SGordon Ross 			 */
464a90cf9f2SGordon Ross 			if (sr->uid_user == NULL) {
465a90cf9f2SGordon Ross 				smb2sr_put_error(sr,
466a90cf9f2SGordon Ross 				    NT_STATUS_INVALID_PARAMETER);
467a90cf9f2SGordon Ross 				goto cmd_done;
468a90cf9f2SGordon Ross 			}
469a90cf9f2SGordon Ross 			sr->smb_uid = sr->uid_user->u_uid;
470a90cf9f2SGordon Ross 		} else {
471a90cf9f2SGordon Ross 			/*
472a90cf9f2SGordon Ross 			 * Lookup the UID
473a90cf9f2SGordon Ross 			 * [MS-SMB2] 3.3.5.2 Verifying the Session
474a90cf9f2SGordon Ross 			 */
475a90cf9f2SGordon Ross 			ASSERT(sr->uid_user == NULL);
476a90cf9f2SGordon Ross 			sr->uid_user = smb_session_lookup_uid(session,
477a90cf9f2SGordon Ross 			    sr->smb_uid);
478a90cf9f2SGordon Ross 			if (sr->uid_user == NULL) {
479a90cf9f2SGordon Ross 				smb2sr_put_error(sr,
480a90cf9f2SGordon Ross 				    NT_STATUS_USER_SESSION_DELETED);
481a90cf9f2SGordon Ross 				goto cmd_done;
482a90cf9f2SGordon Ross 			}
483a90cf9f2SGordon Ross 			sr->user_cr = smb_user_getcred(sr->uid_user);
484a90cf9f2SGordon Ross 		}
485a90cf9f2SGordon Ross 		ASSERT(sr->uid_user != NULL);
486a90cf9f2SGordon Ross 	}
487a90cf9f2SGordon Ross 
488a90cf9f2SGordon Ross 	if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0) {
489a90cf9f2SGordon Ross 		/*
490a90cf9f2SGordon Ross 		 * This command requires a tree connection.
491a90cf9f2SGordon Ross 		 */
492a90cf9f2SGordon Ross 		if (related) {
493a90cf9f2SGordon Ross 			/*
494a90cf9f2SGordon Ross 			 * Previous command should have given us a tree.
495a90cf9f2SGordon Ross 			 * [MS-SMB2] 3.3.5.2 Handling Related Requests
496a90cf9f2SGordon Ross 			 */
497a90cf9f2SGordon Ross 			if (sr->tid_tree == NULL) {
498a90cf9f2SGordon Ross 				smb2sr_put_error(sr,
499a90cf9f2SGordon Ross 				    NT_STATUS_INVALID_PARAMETER);
500a90cf9f2SGordon Ross 				goto cmd_done;
501a90cf9f2SGordon Ross 			}
502a90cf9f2SGordon Ross 			sr->smb_tid = sr->tid_tree->t_tid;
503a90cf9f2SGordon Ross 		} else {
504a90cf9f2SGordon Ross 			/*
505a90cf9f2SGordon Ross 			 * Lookup the TID
506a90cf9f2SGordon Ross 			 * [MS-SMB2] 3.3.5.2 Verifying the Tree Connect
507a90cf9f2SGordon Ross 			 */
508a90cf9f2SGordon Ross 			ASSERT(sr->tid_tree == NULL);
509a90cf9f2SGordon Ross 			sr->tid_tree = smb_session_lookup_tree(session,
510a90cf9f2SGordon Ross 			    sr->smb_tid);
511a90cf9f2SGordon Ross 			if (sr->tid_tree == NULL) {
512a90cf9f2SGordon Ross 				smb2sr_put_error(sr,
513a90cf9f2SGordon Ross 				    NT_STATUS_NETWORK_NAME_DELETED);
514a90cf9f2SGordon Ross 				goto cmd_done;
515a90cf9f2SGordon Ross 			}
516a90cf9f2SGordon Ross 		}
517a90cf9f2SGordon Ross 		ASSERT(sr->tid_tree != NULL);
518a90cf9f2SGordon Ross 	}
519a90cf9f2SGordon Ross 
520a90cf9f2SGordon Ross 	/*
521a90cf9f2SGordon Ross 	 * SMB2 signature verification, two parts:
522a90cf9f2SGordon Ross 	 * (a) Require SMB2_FLAGS_SIGNED (for most request types)
523a90cf9f2SGordon Ross 	 * (b) If SMB2_FLAGS_SIGNED is set, check the signature.
524a90cf9f2SGordon Ross 	 * [MS-SMB2] 3.3.5.2.4 Verifying the Signature
525a90cf9f2SGordon Ross 	 */
526a90cf9f2SGordon Ross 
527a90cf9f2SGordon Ross 	/*
528a90cf9f2SGordon Ross 	 * No user session means no signature check.  That's OK,
529a90cf9f2SGordon Ross 	 * i.e. for commands marked SDDF_SUPPRESS_UID above.
530a90cf9f2SGordon Ross 	 * Note, this also means we won't sign the reply.
531a90cf9f2SGordon Ross 	 */
532a90cf9f2SGordon Ross 	if (sr->uid_user == NULL)
533a90cf9f2SGordon Ross 		sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
534a90cf9f2SGordon Ross 
535a90cf9f2SGordon Ross 	/*
536a90cf9f2SGordon Ross 	 * The SDDF_SUPPRESS_UID dispatch is set for requests that
537a90cf9f2SGordon Ross 	 * don't need a UID (user).  These also don't require a
538a90cf9f2SGordon Ross 	 * signature check here.
539a90cf9f2SGordon Ross 	 */
540a90cf9f2SGordon Ross 	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 &&
541a90cf9f2SGordon Ross 	    sr->uid_user != NULL &&
542a90cf9f2SGordon Ross 	    (sr->uid_user->u_sign_flags & SMB_SIGNING_CHECK) != 0) {
543a90cf9f2SGordon Ross 		/*
544a90cf9f2SGordon Ross 		 * This request type should be signed, and
545a90cf9f2SGordon Ross 		 * we're configured to require signatures.
546a90cf9f2SGordon Ross 		 */
547a90cf9f2SGordon Ross 		if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) == 0) {
548a90cf9f2SGordon Ross 			smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
549a90cf9f2SGordon Ross 			goto cmd_done;
550a90cf9f2SGordon Ross 		}
551a90cf9f2SGordon Ross 		rc = smb2_sign_check_request(sr);
552a90cf9f2SGordon Ross 		if (rc != 0) {
553a90cf9f2SGordon Ross 			DTRACE_PROBE1(smb2__sign__check, smb_request_t, sr);
554a90cf9f2SGordon Ross 			smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
555a90cf9f2SGordon Ross 			goto cmd_done;
556a90cf9f2SGordon Ross 		}
557a90cf9f2SGordon Ross 	}
558a90cf9f2SGordon Ross 
559a90cf9f2SGordon Ross 	/*
560a90cf9f2SGordon Ross 	 * Now that the signing check is done with smb_data,
561a90cf9f2SGordon Ross 	 * advance past the SMB2 header we decoded earlier.
562a90cf9f2SGordon Ross 	 * This leaves sr->smb_data correctly positioned
563a90cf9f2SGordon Ross 	 * for command-specific decoding in the dispatch
564a90cf9f2SGordon Ross 	 * function called next.
565a90cf9f2SGordon Ross 	 */
566a90cf9f2SGordon Ross 	sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE;
567a90cf9f2SGordon Ross 
568a90cf9f2SGordon Ross 	/*
569a90cf9f2SGordon Ross 	 * SMB2 credits determine how many simultaneous commands the
570a90cf9f2SGordon Ross 	 * client may issue, and bounds the range of message IDs those
571a90cf9f2SGordon Ross 	 * commands may use.  With multi-credit support, commands may
572a90cf9f2SGordon Ross 	 * use ranges of message IDs, where the credits used by each
573a90cf9f2SGordon Ross 	 * command are proportional to their data transfer size.
574a90cf9f2SGordon Ross 	 *
575a90cf9f2SGordon Ross 	 * Every command may request an increase or decrease of
576a90cf9f2SGordon Ross 	 * the currently granted credits, based on the difference
577a90cf9f2SGordon Ross 	 * between the credit request and the credit charge.
578a90cf9f2SGordon Ross 	 * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits
579a90cf9f2SGordon Ross 	 *
580a90cf9f2SGordon Ross 	 * Most commands have credit_request=1, credit_charge=1,
581a90cf9f2SGordon Ross 	 * which keeps the credit grant unchanged.
582a90cf9f2SGordon Ross 	 *
583a90cf9f2SGordon Ross 	 * All we're really doing here (for now) is reducing the
584a90cf9f2SGordon Ross 	 * credit_response if the client requests a credit increase
585a90cf9f2SGordon Ross 	 * that would take their credit over the maximum, and
586a90cf9f2SGordon Ross 	 * limiting the decrease so they don't run out of credits.
587a90cf9f2SGordon Ross 	 *
588a90cf9f2SGordon Ross 	 * Later, this could do something dynamic based on load.
589a90cf9f2SGordon Ross 	 *
590a90cf9f2SGordon Ross 	 * One other non-obvious bit about credits: We keep the
591a90cf9f2SGordon Ross 	 * session s_max_credits low until the 1st authentication,
592a90cf9f2SGordon Ross 	 * at which point we'll set the normal maximum_credits.
593a90cf9f2SGordon Ross 	 * Some clients ask for more credits with session setup,
594a90cf9f2SGordon Ross 	 * and we need to handle that requested increase _after_
595a90cf9f2SGordon Ross 	 * the command-specific handler returns so it won't be
596a90cf9f2SGordon Ross 	 * restricted to the lower (pre-auth) limit.
597a90cf9f2SGordon Ross 	 */
598a90cf9f2SGordon Ross 	sr->smb2_credit_response = sr->smb2_credit_request;
599a90cf9f2SGordon Ross 	if (sr->smb2_credit_request < sr->smb2_credit_charge) {
600a90cf9f2SGordon Ross 		uint16_t cur, d;
601a90cf9f2SGordon Ross 
602a90cf9f2SGordon Ross 		mutex_enter(&session->s_credits_mutex);
603a90cf9f2SGordon Ross 		cur = session->s_cur_credits;
604a90cf9f2SGordon Ross 
605a90cf9f2SGordon Ross 		/* Handle credit decrease. */
606a90cf9f2SGordon Ross 		d = sr->smb2_credit_charge - sr->smb2_credit_request;
607a90cf9f2SGordon Ross 		cur -= d;
608a90cf9f2SGordon Ross 		if (cur & 0x8000) {
609a90cf9f2SGordon Ross 			/*
610a90cf9f2SGordon Ross 			 * underflow (bad credit charge or request)
611a90cf9f2SGordon Ross 			 * leave credits unchanged (response=charge)
612a90cf9f2SGordon Ross 			 */
613a90cf9f2SGordon Ross 			cur = session->s_cur_credits;
614a90cf9f2SGordon Ross 			sr->smb2_credit_response = sr->smb2_credit_charge;
615a90cf9f2SGordon Ross 			DTRACE_PROBE1(smb2__credit__neg, smb_request_t, sr);
616a90cf9f2SGordon Ross 		}
617a90cf9f2SGordon Ross 
618a90cf9f2SGordon Ross 		/*
619a90cf9f2SGordon Ross 		 * The server MUST ensure that the number of credits
620a90cf9f2SGordon Ross 		 * held by the client is never reduced to zero.
621a90cf9f2SGordon Ross 		 * [MS-SMB2] 3.3.1.2
622a90cf9f2SGordon Ross 		 */
623a90cf9f2SGordon Ross 		if (cur == 0) {
624a90cf9f2SGordon Ross 			cur = 1;
625a90cf9f2SGordon Ross 			sr->smb2_credit_response += 1;
626a90cf9f2SGordon Ross 			DTRACE_PROBE1(smb2__credit__min, smb_request_t, sr);
627a90cf9f2SGordon Ross 		}
628a90cf9f2SGordon Ross 
629a90cf9f2SGordon Ross 		DTRACE_PROBE3(smb2__credit__decrease,
630a90cf9f2SGordon Ross 		    smb_request_t, sr, int, (int)cur,
631a90cf9f2SGordon Ross 		    int, (int)session->s_cur_credits);
632a90cf9f2SGordon Ross 
633a90cf9f2SGordon Ross 		session->s_cur_credits = cur;
634a90cf9f2SGordon Ross 		mutex_exit(&session->s_credits_mutex);
635a90cf9f2SGordon Ross 	}
636a90cf9f2SGordon Ross 
637a90cf9f2SGordon Ross 	/*
638a90cf9f2SGordon Ross 	 * The real work: call the SMB2 command handler
639a90cf9f2SGordon Ross 	 * (except for "sticky" smb2_status - see above)
640a90cf9f2SGordon Ross 	 */
641a90cf9f2SGordon Ross 	sr->sr_time_start = gethrtime();
642a90cf9f2SGordon Ross 	rc = SDRC_SUCCESS;
643a90cf9f2SGordon Ross 	if (sr->smb2_status == 0) {
644a90cf9f2SGordon Ross 		/* NB: not using pre_op */
645a90cf9f2SGordon Ross 		rc = (*sdd->sdt_function)(sr);
646a90cf9f2SGordon Ross 		/* NB: not using post_op */
647a90cf9f2SGordon Ross 	}
648a90cf9f2SGordon Ross 
649a90cf9f2SGordon Ross 	MBC_FLUSH(&sr->raw_data);
650a90cf9f2SGordon Ross 
651a90cf9f2SGordon Ross 	/*
652a90cf9f2SGordon Ross 	 * Second half of SMB2 credit handling (increases)
653a90cf9f2SGordon Ross 	 */
654a90cf9f2SGordon Ross 	if (sr->smb2_credit_request > sr->smb2_credit_charge) {
655a90cf9f2SGordon Ross 		uint16_t cur, d;
656a90cf9f2SGordon Ross 
657a90cf9f2SGordon Ross 		mutex_enter(&session->s_credits_mutex);
658a90cf9f2SGordon Ross 		cur = session->s_cur_credits;
659a90cf9f2SGordon Ross 
660a90cf9f2SGordon Ross 		/* Handle credit increase. */
661a90cf9f2SGordon Ross 		d = sr->smb2_credit_request - sr->smb2_credit_charge;
662a90cf9f2SGordon Ross 		cur += d;
663a90cf9f2SGordon Ross 
664a90cf9f2SGordon Ross 		/*
665a90cf9f2SGordon Ross 		 * If new credits would be above max,
666a90cf9f2SGordon Ross 		 * reduce the credit grant.
667a90cf9f2SGordon Ross 		 */
668a90cf9f2SGordon Ross 		if (cur > session->s_max_credits) {
669a90cf9f2SGordon Ross 			d = cur - session->s_max_credits;
670a90cf9f2SGordon Ross 			cur = session->s_max_credits;
671a90cf9f2SGordon Ross 			sr->smb2_credit_response -= d;
672a90cf9f2SGordon Ross 			DTRACE_PROBE1(smb2__credit__max, smb_request_t, sr);
673a90cf9f2SGordon Ross 		}
674a90cf9f2SGordon Ross 
675a90cf9f2SGordon Ross 		DTRACE_PROBE3(smb2__credit__increase,
676a90cf9f2SGordon Ross 		    smb_request_t, sr, int, (int)cur,
677a90cf9f2SGordon Ross 		    int, (int)session->s_cur_credits);
678a90cf9f2SGordon Ross 
679a90cf9f2SGordon Ross 		session->s_cur_credits = cur;
680a90cf9f2SGordon Ross 		mutex_exit(&session->s_credits_mutex);
681a90cf9f2SGordon Ross 	}
682a90cf9f2SGordon Ross 
683a90cf9f2SGordon Ross cmd_done:
684a90cf9f2SGordon Ross 	/*
685a90cf9f2SGordon Ross 	 * Pad the reply to align(8) if necessary.
686a90cf9f2SGordon Ross 	 */
687a90cf9f2SGordon Ross 	if (sr->reply.chain_offset & 7) {
688a90cf9f2SGordon Ross 		int padsz = 8 - (sr->reply.chain_offset & 7);
689a90cf9f2SGordon Ross 		(void) smb_mbc_encodef(&sr->reply, "#.", padsz);
690a90cf9f2SGordon Ross 	}
691a90cf9f2SGordon Ross 	ASSERT((sr->reply.chain_offset & 7) == 0);
692a90cf9f2SGordon Ross 
693a90cf9f2SGordon Ross 	/*
694a90cf9f2SGordon Ross 	 * Record some statistics: latency, rx bytes, tx bytes.
695a90cf9f2SGordon Ross 	 */
696*ac2bf314SMatt Barden 	smb_server_inc_req(sr->sr_server);
697a90cf9f2SGordon Ross 	smb_latency_add_sample(&sds->sdt_lat,
698a90cf9f2SGordon Ross 	    gethrtime() - sr->sr_time_start);
699a90cf9f2SGordon Ross 	atomic_add_64(&sds->sdt_rxb,
700a90cf9f2SGordon Ross 	    (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr));
701a90cf9f2SGordon Ross 	atomic_add_64(&sds->sdt_txb,
702a90cf9f2SGordon Ross 	    (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr));
703a90cf9f2SGordon Ross 
704a90cf9f2SGordon Ross 	switch (rc) {
705a90cf9f2SGordon Ross 	case SDRC_SUCCESS:
706a90cf9f2SGordon Ross 		break;
707a90cf9f2SGordon Ross 	default:
708a90cf9f2SGordon Ross 		/*
709a90cf9f2SGordon Ross 		 * SMB2 does not use the other dispatch return codes.
710a90cf9f2SGordon Ross 		 * If we see something else, log an event so we'll
711a90cf9f2SGordon Ross 		 * know something is returning bogus status codes.
712a90cf9f2SGordon Ross 		 * If you see these in the log, use dtrace to find
713a90cf9f2SGordon Ross 		 * the code returning something else.
714a90cf9f2SGordon Ross 		 */
715a90cf9f2SGordon Ross #ifdef	DEBUG
716a90cf9f2SGordon Ross 		cmn_err(CE_NOTE, "handler for %u returned 0x%x",
717a90cf9f2SGordon Ross 		    sr->smb2_cmd_code, rc);
718a90cf9f2SGordon Ross #endif
719a90cf9f2SGordon Ross 		/* FALLTHROUGH */
720a90cf9f2SGordon Ross 	case SDRC_ERROR:
721a90cf9f2SGordon Ross 		if (sr->smb2_status == 0)
722a90cf9f2SGordon Ross 			sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
723a90cf9f2SGordon Ross 		break;
724a90cf9f2SGordon Ross 	case SDRC_DROP_VC:
725a90cf9f2SGordon Ross 		disconnect = B_TRUE;
726a90cf9f2SGordon Ross 		goto cleanup;
727a90cf9f2SGordon Ross 	}
728a90cf9f2SGordon Ross 
729a90cf9f2SGordon Ross 	/*
730a90cf9f2SGordon Ross 	 * If there's a next command, figure out where it starts,
731a90cf9f2SGordon Ross 	 * and fill in the next command offset for the reply.
732a90cf9f2SGordon Ross 	 * Note: We sanity checked smb2_next_command above
733a90cf9f2SGordon Ross 	 * (the offset to the next command).  Similarly set
734a90cf9f2SGordon Ross 	 * smb2_next_reply as the offset to the next reply.
735a90cf9f2SGordon Ross 	 */
736a90cf9f2SGordon Ross 	if (sr->smb2_next_command != 0) {
737a90cf9f2SGordon Ross 		sr->command.chain_offset =
738a90cf9f2SGordon Ross 		    sr->smb2_cmd_hdr + sr->smb2_next_command;
739a90cf9f2SGordon Ross 		sr->smb2_next_reply =
740a90cf9f2SGordon Ross 		    sr->reply.chain_offset - sr->smb2_reply_hdr;
741a90cf9f2SGordon Ross 	} else {
742a90cf9f2SGordon Ross 		sr->smb2_next_reply = 0;
743a90cf9f2SGordon Ross 	}
744a90cf9f2SGordon Ross 
745a90cf9f2SGordon Ross 	/*
746a90cf9f2SGordon Ross 	 * Overwrite the SMB2 header for the response of
747a90cf9f2SGordon Ross 	 * this command (possibly part of a compound).
748a90cf9f2SGordon Ross 	 * encode_header adds: SMB2_FLAGS_SERVER_TO_REDIR
749a90cf9f2SGordon Ross 	 */
750a90cf9f2SGordon Ross 	(void) smb2_encode_header(sr, B_TRUE);
751a90cf9f2SGordon Ross 
752a90cf9f2SGordon Ross 	if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
753a90cf9f2SGordon Ross 		smb2_sign_reply(sr);
754a90cf9f2SGordon Ross 
755a90cf9f2SGordon Ross 	if (sr->smb2_next_command != 0)
756a90cf9f2SGordon Ross 		goto cmd_start;
757a90cf9f2SGordon Ross 
758a90cf9f2SGordon Ross 	/*
759a90cf9f2SGordon Ross 	 * We've done all the commands in this compound.
760a90cf9f2SGordon Ross 	 * Send it out.
761a90cf9f2SGordon Ross 	 */
762a90cf9f2SGordon Ross 	smb2_send_reply(sr);
763a90cf9f2SGordon Ross 
764a90cf9f2SGordon Ross 	/*
765a90cf9f2SGordon Ross 	 * If any of the requests "went async", process those now.
766a90cf9f2SGordon Ross 	 * The async. function "keeps" this sr, changing its state
767a90cf9f2SGordon Ross 	 * to completed and calling smb_request_free().
768a90cf9f2SGordon Ross 	 */
769a90cf9f2SGordon Ross 	if (sr->sr_async_req != NULL) {
770a90cf9f2SGordon Ross 		smb2sr_do_async(sr);
771a90cf9f2SGordon Ross 		return;
772a90cf9f2SGordon Ross 	}
773a90cf9f2SGordon Ross 
774a90cf9f2SGordon Ross cleanup:
775a90cf9f2SGordon Ross 	if (disconnect) {
776a90cf9f2SGordon Ross 		smb_rwx_rwenter(&session->s_lock, RW_WRITER);
777a90cf9f2SGordon Ross 		switch (session->s_state) {
778a90cf9f2SGordon Ross 		case SMB_SESSION_STATE_DISCONNECTED:
779a90cf9f2SGordon Ross 		case SMB_SESSION_STATE_TERMINATED:
780a90cf9f2SGordon Ross 			break;
781a90cf9f2SGordon Ross 		default:
782a90cf9f2SGordon Ross 			smb_soshutdown(session->sock);
783a90cf9f2SGordon Ross 			session->s_state = SMB_SESSION_STATE_DISCONNECTED;
784a90cf9f2SGordon Ross 			break;
785a90cf9f2SGordon Ross 		}
786a90cf9f2SGordon Ross 		smb_rwx_rwexit(&session->s_lock);
787a90cf9f2SGordon Ross 	}
788a90cf9f2SGordon Ross 
789a90cf9f2SGordon Ross 	mutex_enter(&sr->sr_mutex);
790a90cf9f2SGordon Ross 	sr->sr_state = SMB_REQ_STATE_COMPLETED;
791a90cf9f2SGordon Ross 	mutex_exit(&sr->sr_mutex);
792a90cf9f2SGordon Ross 
793a90cf9f2SGordon Ross 	smb_request_free(sr);
794a90cf9f2SGordon Ross }
795a90cf9f2SGordon Ross 
796a90cf9f2SGordon Ross /*
797a90cf9f2SGordon Ross  * Dispatch an async request using saved information.
798a90cf9f2SGordon Ross  * See smb2sr_save_async and [MS-SMB2] 3.3.4.2
799a90cf9f2SGordon Ross  *
800a90cf9f2SGordon Ross  * This is sort of a "lite" version of smb2sr_work.  Initialize the
801a90cf9f2SGordon Ross  * command and reply areas as they were when the command-speicific
802a90cf9f2SGordon Ross  * handler started (in case it needs to decode anything again).
803a90cf9f2SGordon Ross  * Call the async function, which builds the command-specific part
804a90cf9f2SGordon Ross  * of the response.  Finally, send the response and free the sr.
805a90cf9f2SGordon Ross  */
806a90cf9f2SGordon Ross void
807a90cf9f2SGordon Ross smb2sr_do_async(smb_request_t *sr)
808a90cf9f2SGordon Ross {
809a90cf9f2SGordon Ross 	const smb_disp_entry_t	*sdd;
810a90cf9f2SGordon Ross 	smb_disp_stats_t	*sds;
811a90cf9f2SGordon Ross 	smb2_async_req_t	*ar;
812a90cf9f2SGordon Ross 	int rc = 0;
813a90cf9f2SGordon Ross 
814a90cf9f2SGordon Ross 	/*
815a90cf9f2SGordon Ross 	 * Restore what smb2_decode_header found.
816a90cf9f2SGordon Ross 	 * (In lieu of decoding it again.)
817a90cf9f2SGordon Ross 	 */
818a90cf9f2SGordon Ross 	ar = sr->sr_async_req;
819a90cf9f2SGordon Ross 	sr->smb2_cmd_hdr   = ar->ar_cmd_hdr;
820a90cf9f2SGordon Ross 	sr->smb2_cmd_code  = ar->ar_cmd_code;
821a90cf9f2SGordon Ross 	sr->smb2_hdr_flags = ar->ar_hdr_flags;
822a90cf9f2SGordon Ross 	sr->smb2_async_id  = (uintptr_t)ar;
823a90cf9f2SGordon Ross 	sr->smb2_messageid = ar->ar_messageid;
824a90cf9f2SGordon Ross 	sr->smb_pid = ar->ar_pid;
825a90cf9f2SGordon Ross 	sr->smb_tid = ar->ar_tid;
826a90cf9f2SGordon Ross 	sr->smb_uid = ar->ar_uid;
827a90cf9f2SGordon Ross 	sr->smb2_status = 0;
828a90cf9f2SGordon Ross 
829a90cf9f2SGordon Ross 	/*
830a90cf9f2SGordon Ross 	 * Async requests don't grant credits, because any credits
831a90cf9f2SGordon Ross 	 * should have gone out with the interim reply.
832a90cf9f2SGordon Ross 	 * An async reply goes alone (no next reply).
833a90cf9f2SGordon Ross 	 */
834a90cf9f2SGordon Ross 	sr->smb2_credit_response = 0;
835a90cf9f2SGordon Ross 	sr->smb2_next_reply = 0;
836a90cf9f2SGordon Ross 
837a90cf9f2SGordon Ross 	/*
838a90cf9f2SGordon Ross 	 * Setup input mbuf_chain
839a90cf9f2SGordon Ross 	 */
840a90cf9f2SGordon Ross 	ASSERT(ar->ar_cmd_len >= SMB2_HDR_SIZE);
841a90cf9f2SGordon Ross 	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
842a90cf9f2SGordon Ross 	    sr->smb2_cmd_hdr + SMB2_HDR_SIZE,
843a90cf9f2SGordon Ross 	    ar->ar_cmd_len - SMB2_HDR_SIZE);
844a90cf9f2SGordon Ross 
845a90cf9f2SGordon Ross 	/*
846a90cf9f2SGordon Ross 	 * Setup output mbuf_chain
847a90cf9f2SGordon Ross 	 */
848a90cf9f2SGordon Ross 	MBC_FLUSH(&sr->reply);
849a90cf9f2SGordon Ross 	sr->smb2_reply_hdr = sr->reply.chain_offset;
850a90cf9f2SGordon Ross 	(void) smb2_encode_header(sr, B_FALSE);
851a90cf9f2SGordon Ross 
852a90cf9f2SGordon Ross 	VERIFY3U(sr->smb2_cmd_code, <, SMB2_INVALID_CMD);
853a90cf9f2SGordon Ross 	sdd = &smb2_disp_table[sr->smb2_cmd_code];
854a90cf9f2SGordon Ross 	sds = sr->session->s_server->sv_disp_stats2;
855a90cf9f2SGordon Ross 	sds = &sds[sr->smb2_cmd_code];
856a90cf9f2SGordon Ross 
857a90cf9f2SGordon Ross 	/*
858a90cf9f2SGordon Ross 	 * Keep the UID, TID, ofile we have.
859a90cf9f2SGordon Ross 	 */
860a90cf9f2SGordon Ross 	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 &&
861a90cf9f2SGordon Ross 	    sr->uid_user == NULL) {
862a90cf9f2SGordon Ross 		smb2sr_put_error(sr, NT_STATUS_USER_SESSION_DELETED);
863a90cf9f2SGordon Ross 		goto cmd_done;
864a90cf9f2SGordon Ross 	}
865a90cf9f2SGordon Ross 	if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0 &&
866a90cf9f2SGordon Ross 	    sr->tid_tree == NULL) {
867a90cf9f2SGordon Ross 		smb2sr_put_error(sr, NT_STATUS_NETWORK_NAME_DELETED);
868a90cf9f2SGordon Ross 		goto cmd_done;
869a90cf9f2SGordon Ross 	}
870a90cf9f2SGordon Ross 
871a90cf9f2SGordon Ross 	/*
872a90cf9f2SGordon Ross 	 * Signature already verified
873a90cf9f2SGordon Ross 	 * Credits handled...
874a90cf9f2SGordon Ross 	 *
875a90cf9f2SGordon Ross 	 * Just call the async handler function.
876a90cf9f2SGordon Ross 	 */
877a90cf9f2SGordon Ross 	rc = ar->ar_func(sr);
878a90cf9f2SGordon Ross 	if (rc != 0 && sr->smb2_status == 0)
879a90cf9f2SGordon Ross 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
880a90cf9f2SGordon Ross 
881a90cf9f2SGordon Ross cmd_done:
882a90cf9f2SGordon Ross 	/*
883a90cf9f2SGordon Ross 	 * Pad the reply to align(8) if necessary.
884a90cf9f2SGordon Ross 	 */
885a90cf9f2SGordon Ross 	if (sr->reply.chain_offset & 7) {
886a90cf9f2SGordon Ross 		int padsz = 8 - (sr->reply.chain_offset & 7);
887a90cf9f2SGordon Ross 		(void) smb_mbc_encodef(&sr->reply, "#.", padsz);
888a90cf9f2SGordon Ross 	}
889a90cf9f2SGordon Ross 	ASSERT((sr->reply.chain_offset & 7) == 0);
890a90cf9f2SGordon Ross 
891a90cf9f2SGordon Ross 	/*
892a90cf9f2SGordon Ross 	 * Record some statistics: (just tx bytes here)
893a90cf9f2SGordon Ross 	 */
894a90cf9f2SGordon Ross 	atomic_add_64(&sds->sdt_txb,
895a90cf9f2SGordon Ross 	    (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr));
896a90cf9f2SGordon Ross 
897a90cf9f2SGordon Ross 	/*
898a90cf9f2SGordon Ross 	 * Overwrite the SMB2 header for the response of
899a90cf9f2SGordon Ross 	 * this command (possibly part of a compound).
900a90cf9f2SGordon Ross 	 * The call adds: SMB2_FLAGS_SERVER_TO_REDIR
901a90cf9f2SGordon Ross 	 */
902a90cf9f2SGordon Ross 	(void) smb2_encode_header(sr, B_TRUE);
903a90cf9f2SGordon Ross 
904a90cf9f2SGordon Ross 	if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
905a90cf9f2SGordon Ross 		smb2_sign_reply(sr);
906a90cf9f2SGordon Ross 
907a90cf9f2SGordon Ross 	smb2_send_reply(sr);
908a90cf9f2SGordon Ross 
909a90cf9f2SGordon Ross 	/*
910a90cf9f2SGordon Ross 	 * Done.  Unlink and free.
911a90cf9f2SGordon Ross 	 */
912a90cf9f2SGordon Ross 	sr->sr_async_req = NULL;
913a90cf9f2SGordon Ross 	kmem_free(ar, sizeof (*ar));
914a90cf9f2SGordon Ross 
915a90cf9f2SGordon Ross 	mutex_enter(&sr->sr_mutex);
916a90cf9f2SGordon Ross 	sr->sr_state = SMB_REQ_STATE_COMPLETED;
917a90cf9f2SGordon Ross 	mutex_exit(&sr->sr_mutex);
918a90cf9f2SGordon Ross 
919a90cf9f2SGordon Ross 	smb_request_free(sr);
920a90cf9f2SGordon Ross }
921a90cf9f2SGordon Ross 
922a90cf9f2SGordon Ross /*
923a90cf9f2SGordon Ross  * In preparation for sending an "interim response", save
924a90cf9f2SGordon Ross  * all the state we'll need to run an async command later,
925a90cf9f2SGordon Ross  * and assign an "async id" for this (now async) command.
926a90cf9f2SGordon Ross  * See [MS-SMB2] 3.3.4.2
927a90cf9f2SGordon Ross  *
928a90cf9f2SGordon Ross  * If more than one request in a compound request tries to
929a90cf9f2SGordon Ross  * "go async", we can "say no".  See [MS-SMB2] 3.3.4.2
930a90cf9f2SGordon Ross  *	If an operation would require asynchronous processing
931a90cf9f2SGordon Ross  *	but resources are constrained, the server MAY choose to
932a90cf9f2SGordon Ross  *	fail that operation with STATUS_INSUFFICIENT_RESOURCES.
933a90cf9f2SGordon Ross  *
934a90cf9f2SGordon Ross  * For simplicity, we further restrict the cases where we're
935a90cf9f2SGordon Ross  * willing to "go async", and only allow the last command in a
936a90cf9f2SGordon Ross  * compound to "go async".  It happens that this is the only
937a90cf9f2SGordon Ross  * case where we're actually asked to go async anyway. This
938a90cf9f2SGordon Ross  * simplification also means there can be at most one command
939a90cf9f2SGordon Ross  * in a compound that "goes async" (the last one).
940a90cf9f2SGordon Ross  *
941a90cf9f2SGordon Ross  * If we agree to "go async", this should return STATUS_PENDING.
942a90cf9f2SGordon Ross  * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and
943a90cf9f2SGordon Ross  * all requests following this request.  (See the comments re.
944a90cf9f2SGordon Ross  * "sticky" smb2_status values in smb2sr_work).
945a90cf9f2SGordon Ross  *
946a90cf9f2SGordon Ross  * Note: the Async ID we assign here is arbitrary, and need only
947a90cf9f2SGordon Ross  * be unique among pending async responses on this connection, so
948a90cf9f2SGordon Ross  * this just uses an object address as the Async ID.
949a90cf9f2SGordon Ross  *
950a90cf9f2SGordon Ross  * Also, the assigned worker is the ONLY thread using this
951a90cf9f2SGordon Ross  * async request object (sr_async_req) so no locking.
952a90cf9f2SGordon Ross  */
953a90cf9f2SGordon Ross uint32_t
954a90cf9f2SGordon Ross smb2sr_go_async(smb_request_t *sr,
955adb064afSToomas Soome     smb_sdrc_t (*async_func)(smb_request_t *))
956a90cf9f2SGordon Ross {
957a90cf9f2SGordon Ross 	smb2_async_req_t *ar;
958a90cf9f2SGordon Ross 
959a90cf9f2SGordon Ross 	if (sr->smb2_next_command != 0)
960a90cf9f2SGordon Ross 		return (NT_STATUS_INSUFFICIENT_RESOURCES);
961a90cf9f2SGordon Ross 
962a90cf9f2SGordon Ross 	ASSERT(sr->sr_async_req == NULL);
963a90cf9f2SGordon Ross 	ar = kmem_zalloc(sizeof (*ar), KM_SLEEP);
964a90cf9f2SGordon Ross 
965a90cf9f2SGordon Ross 	/*
966a90cf9f2SGordon Ross 	 * Place an interim response in the compound reply.
967a90cf9f2SGordon Ross 	 *
968a90cf9f2SGordon Ross 	 * Turn on the "async" flag for both the (synchronous)
969a90cf9f2SGordon Ross 	 * interim response and the (later) async response,
970a90cf9f2SGordon Ross 	 * by storing that in flags before coping into ar.
971a90cf9f2SGordon Ross 	 */
972a90cf9f2SGordon Ross 	sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND;
973a90cf9f2SGordon Ross 	sr->smb2_async_id = (uintptr_t)ar;
974a90cf9f2SGordon Ross 
975a90cf9f2SGordon Ross 	ar->ar_func = async_func;
976a90cf9f2SGordon Ross 	ar->ar_cmd_hdr = sr->smb2_cmd_hdr;
977a90cf9f2SGordon Ross 	ar->ar_cmd_len = sr->smb_data.max_bytes - sr->smb2_cmd_hdr;
978a90cf9f2SGordon Ross 
979a90cf9f2SGordon Ross 	ar->ar_cmd_code = sr->smb2_cmd_code;
98058ccc3dcSGordon Ross 	ar->ar_hdr_flags = sr->smb2_hdr_flags;
981a90cf9f2SGordon Ross 	ar->ar_messageid = sr->smb2_messageid;
982a90cf9f2SGordon Ross 	ar->ar_pid = sr->smb_pid;
983a90cf9f2SGordon Ross 	ar->ar_tid = sr->smb_tid;
984a90cf9f2SGordon Ross 	ar->ar_uid = sr->smb_uid;
985a90cf9f2SGordon Ross 
986a90cf9f2SGordon Ross 	sr->sr_async_req = ar;
987a90cf9f2SGordon Ross 
988a90cf9f2SGordon Ross 	/* Interim responses are NOT signed. */
989a90cf9f2SGordon Ross 	sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
990a90cf9f2SGordon Ross 
991a90cf9f2SGordon Ross 	return (NT_STATUS_PENDING);
992a90cf9f2SGordon Ross }
993a90cf9f2SGordon Ross 
994a90cf9f2SGordon Ross int
995a90cf9f2SGordon Ross smb2_decode_header(smb_request_t *sr)
996a90cf9f2SGordon Ross {
997a90cf9f2SGordon Ross 	uint64_t ssnid;
998a90cf9f2SGordon Ross 	uint32_t pid, tid;
999a90cf9f2SGordon Ross 	uint16_t hdr_len;
1000a90cf9f2SGordon Ross 	int rc;
1001a90cf9f2SGordon Ross 
1002a90cf9f2SGordon Ross 	rc = smb_mbc_decodef(
1003a90cf9f2SGordon Ross 	    &sr->command, "Nwww..wwllqllq16c",
1004a90cf9f2SGordon Ross 	    &hdr_len,			/* w */
1005a90cf9f2SGordon Ross 	    &sr->smb2_credit_charge,	/* w */
1006a90cf9f2SGordon Ross 	    &sr->smb2_chan_seq,		/* w */
1007a90cf9f2SGordon Ross 	    /* reserved			  .. */
1008a90cf9f2SGordon Ross 	    &sr->smb2_cmd_code,		/* w */
1009a90cf9f2SGordon Ross 	    &sr->smb2_credit_request,	/* w */
1010a90cf9f2SGordon Ross 	    &sr->smb2_hdr_flags,	/* l */
1011a90cf9f2SGordon Ross 	    &sr->smb2_next_command,	/* l */
1012a90cf9f2SGordon Ross 	    &sr->smb2_messageid,	/* q */
1013a90cf9f2SGordon Ross 	    &pid,			/* l */
1014a90cf9f2SGordon Ross 	    &tid,			/* l */
1015a90cf9f2SGordon Ross 	    &ssnid,			/* q */
1016a90cf9f2SGordon Ross 	    sr->smb2_sig);		/* 16c */
1017a90cf9f2SGordon Ross 	if (rc)
1018a90cf9f2SGordon Ross 		return (rc);
1019a90cf9f2SGordon Ross 
1020a90cf9f2SGordon Ross 	if (hdr_len != SMB2_HDR_SIZE)
1021a90cf9f2SGordon Ross 		return (-1);
1022a90cf9f2SGordon Ross 
1023a90cf9f2SGordon Ross 	sr->smb_uid = (uint16_t)ssnid;	/* XXX wide UIDs */
1024a90cf9f2SGordon Ross 
1025a90cf9f2SGordon Ross 	if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1026a90cf9f2SGordon Ross 		sr->smb2_async_id = pid |
1027a90cf9f2SGordon Ross 		    ((uint64_t)tid) << 32;
1028a90cf9f2SGordon Ross 	} else {
1029a90cf9f2SGordon Ross 		sr->smb_pid = pid;
1030a90cf9f2SGordon Ross 		sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */
1031a90cf9f2SGordon Ross 	}
1032a90cf9f2SGordon Ross 
1033a90cf9f2SGordon Ross 	return (rc);
1034a90cf9f2SGordon Ross }
1035a90cf9f2SGordon Ross 
1036a90cf9f2SGordon Ross int
1037a90cf9f2SGordon Ross smb2_encode_header(smb_request_t *sr, boolean_t overwrite)
1038a90cf9f2SGordon Ross {
1039a90cf9f2SGordon Ross 	uint64_t ssnid = sr->smb_uid;
1040a90cf9f2SGordon Ross 	uint64_t pid_tid_aid; /* pid+tid, or async id */
1041a90cf9f2SGordon Ross 	uint32_t reply_hdr_flags;
1042a90cf9f2SGordon Ross 	int rc;
1043a90cf9f2SGordon Ross 
1044a90cf9f2SGordon Ross 	if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1045a90cf9f2SGordon Ross 		pid_tid_aid = sr->smb2_async_id;
1046a90cf9f2SGordon Ross 	} else {
1047a90cf9f2SGordon Ross 		pid_tid_aid = sr->smb_pid |
1048a90cf9f2SGordon Ross 		    ((uint64_t)sr->smb_tid) << 32;
1049a90cf9f2SGordon Ross 	}
1050a90cf9f2SGordon Ross 	reply_hdr_flags = sr->smb2_hdr_flags | SMB2_FLAGS_SERVER_TO_REDIR;
1051a90cf9f2SGordon Ross 
1052a90cf9f2SGordon Ross 	if (overwrite) {
1053a90cf9f2SGordon Ross 		rc = smb_mbc_poke(&sr->reply,
1054a90cf9f2SGordon Ross 		    sr->smb2_reply_hdr,
1055a90cf9f2SGordon Ross 		    "Nwwlwwllqqq16c",
1056a90cf9f2SGordon Ross 		    SMB2_HDR_SIZE,		/* w */
1057a90cf9f2SGordon Ross 		    sr->smb2_credit_charge,	/* w */
1058a90cf9f2SGordon Ross 		    sr->smb2_status,		/* l */
1059a90cf9f2SGordon Ross 		    sr->smb2_cmd_code,		/* w */
1060a90cf9f2SGordon Ross 		    sr->smb2_credit_response,	/* w */
1061a90cf9f2SGordon Ross 		    reply_hdr_flags,		/* l */
1062a90cf9f2SGordon Ross 		    sr->smb2_next_reply,	/* l */
1063a90cf9f2SGordon Ross 		    sr->smb2_messageid,		/* q */
1064a90cf9f2SGordon Ross 		    pid_tid_aid,		/* q */
1065a90cf9f2SGordon Ross 		    ssnid,			/* q */
1066a90cf9f2SGordon Ross 		    sr->smb2_sig);		/* 16c */
1067a90cf9f2SGordon Ross 	} else {
1068a90cf9f2SGordon Ross 		rc = smb_mbc_encodef(&sr->reply,
1069a90cf9f2SGordon Ross 		    "Nwwlwwllqqq16c",
1070a90cf9f2SGordon Ross 		    SMB2_HDR_SIZE,		/* w */
1071a90cf9f2SGordon Ross 		    sr->smb2_credit_charge,	/* w */
1072a90cf9f2SGordon Ross 		    sr->smb2_status,		/* l */
1073a90cf9f2SGordon Ross 		    sr->smb2_cmd_code,		/* w */
1074a90cf9f2SGordon Ross 		    sr->smb2_credit_response,	/* w */
1075a90cf9f2SGordon Ross 		    reply_hdr_flags,		/* l */
1076a90cf9f2SGordon Ross 		    sr->smb2_next_reply,	/* l */
1077a90cf9f2SGordon Ross 		    sr->smb2_messageid,		/* q */
1078a90cf9f2SGordon Ross 		    pid_tid_aid,		/* q */
1079a90cf9f2SGordon Ross 		    ssnid,			/* q */
1080a90cf9f2SGordon Ross 		    sr->smb2_sig);		/* 16c */
1081a90cf9f2SGordon Ross 	}
1082a90cf9f2SGordon Ross 
1083a90cf9f2SGordon Ross 	return (rc);
1084a90cf9f2SGordon Ross }
1085a90cf9f2SGordon Ross 
1086a90cf9f2SGordon Ross void
1087a90cf9f2SGordon Ross smb2_send_reply(smb_request_t *sr)
1088a90cf9f2SGordon Ross {
1089a90cf9f2SGordon Ross 
1090a90cf9f2SGordon Ross 	if (smb_session_send(sr->session, 0, &sr->reply) == 0)
1091a90cf9f2SGordon Ross 		sr->reply.chain = 0;
1092a90cf9f2SGordon Ross }
1093a90cf9f2SGordon Ross 
1094a90cf9f2SGordon Ross /*
1095a90cf9f2SGordon Ross  * This wrapper function exists to help catch calls to smbsr_status()
1096a90cf9f2SGordon Ross  * (which is SMB1-specific) in common code.  See smbsr_status().
1097a90cf9f2SGordon Ross  * If the log message below is seen, put a dtrace probe on this
1098a90cf9f2SGordon Ross  * function with a stack() action to see who is calling the SMB1
1099a90cf9f2SGordon Ross  * "put error" from common code, and fix it.
1100a90cf9f2SGordon Ross  */
1101a90cf9f2SGordon Ross void
1102a90cf9f2SGordon Ross smbsr_status_smb2(smb_request_t *sr, DWORD status)
1103a90cf9f2SGordon Ross {
1104a90cf9f2SGordon Ross 	const char *name;
1105a90cf9f2SGordon Ross 
1106a90cf9f2SGordon Ross 	if (sr->smb2_cmd_code < SMB2__NCMDS)
1107a90cf9f2SGordon Ross 		name = smb2_disp_table[sr->smb2_cmd_code].sdt_name;
1108a90cf9f2SGordon Ross 	else
1109a90cf9f2SGordon Ross 		name = "<unknown>";
1110a90cf9f2SGordon Ross #ifdef	DEBUG
1111a90cf9f2SGordon Ross 	cmn_err(CE_NOTE, "smbsr_status called for %s", name);
1112a90cf9f2SGordon Ross #endif
1113a90cf9f2SGordon Ross 
1114a90cf9f2SGordon Ross 	smb2sr_put_error_data(sr, status, NULL);
1115a90cf9f2SGordon Ross }
1116a90cf9f2SGordon Ross 
1117a90cf9f2SGordon Ross void
1118a90cf9f2SGordon Ross smb2sr_put_errno(struct smb_request *sr, int errnum)
1119a90cf9f2SGordon Ross {
1120a90cf9f2SGordon Ross 	uint32_t status = smb_errno2status(errnum);
1121a90cf9f2SGordon Ross 	smb2sr_put_error_data(sr, status, NULL);
1122a90cf9f2SGordon Ross }
1123a90cf9f2SGordon Ross 
1124a90cf9f2SGordon Ross void
1125a90cf9f2SGordon Ross smb2sr_put_error(smb_request_t *sr, uint32_t status)
1126a90cf9f2SGordon Ross {
1127a90cf9f2SGordon Ross 	smb2sr_put_error_data(sr, status, NULL);
1128a90cf9f2SGordon Ross }
1129a90cf9f2SGordon Ross 
1130a90cf9f2SGordon Ross /*
1131a90cf9f2SGordon Ross  * Build an SMB2 error response.  [MS-SMB2] 2.2.2
1132a90cf9f2SGordon Ross  */
1133a90cf9f2SGordon Ross void
1134a90cf9f2SGordon Ross smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc)
1135a90cf9f2SGordon Ross {
1136a90cf9f2SGordon Ross 	DWORD len;
1137a90cf9f2SGordon Ross 
1138a90cf9f2SGordon Ross 	/*
1139a90cf9f2SGordon Ross 	 * The common dispatch code writes this when it
1140a90cf9f2SGordon Ross 	 * updates the SMB2 header before sending.
1141a90cf9f2SGordon Ross 	 */
1142a90cf9f2SGordon Ross 	sr->smb2_status = status;
1143a90cf9f2SGordon Ross 
1144a90cf9f2SGordon Ross 	/* Rewind to the end of the SMB header. */
1145a90cf9f2SGordon Ross 	sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE;
1146a90cf9f2SGordon Ross 
1147a90cf9f2SGordon Ross 	/*
1148a90cf9f2SGordon Ross 	 * NB: Must provide at least one byte of error data,
1149a90cf9f2SGordon Ross 	 * per [MS-SMB2] 2.2.2
1150a90cf9f2SGordon Ross 	 */
1151a90cf9f2SGordon Ross 	if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) {
1152a90cf9f2SGordon Ross 		(void) smb_mbc_encodef(
1153a90cf9f2SGordon Ross 		    &sr->reply,
1154a90cf9f2SGordon Ross 		    "wwlC",
1155a90cf9f2SGordon Ross 		    9,	/* StructSize */	/* w */
1156a90cf9f2SGordon Ross 		    0,	/* reserved */		/* w */
1157a90cf9f2SGordon Ross 		    len,			/* l */
1158a90cf9f2SGordon Ross 		    mbc);			/* C */
1159a90cf9f2SGordon Ross 	} else {
1160a90cf9f2SGordon Ross 		(void) smb_mbc_encodef(
1161a90cf9f2SGordon Ross 		    &sr->reply,
1162a90cf9f2SGordon Ross 		    "wwl.",
1163a90cf9f2SGordon Ross 		    9,	/* StructSize */	/* w */
1164a90cf9f2SGordon Ross 		    0,	/* reserved */		/* w */
1165a90cf9f2SGordon Ross 		    0);				/* l. */
1166a90cf9f2SGordon Ross 	}
1167a90cf9f2SGordon Ross }
1168a90cf9f2SGordon Ross 
1169a90cf9f2SGordon Ross /*
1170a90cf9f2SGordon Ross  * smb2sr_lookup_fid
1171a90cf9f2SGordon Ross  *
1172a90cf9f2SGordon Ross  * Setup sr->fid_ofile, either inherited from a related command,
1173a90cf9f2SGordon Ross  * or obtained via FID lookup.  Similar inheritance logic as in
1174a90cf9f2SGordon Ross  * smb2sr_work.
1175a90cf9f2SGordon Ross  */
1176a90cf9f2SGordon Ross uint32_t
1177a90cf9f2SGordon Ross smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid)
1178a90cf9f2SGordon Ross {
1179a90cf9f2SGordon Ross 	boolean_t related = sr->smb2_hdr_flags &
1180a90cf9f2SGordon Ross 	    SMB2_FLAGS_RELATED_OPERATIONS;
1181a90cf9f2SGordon Ross 
1182a90cf9f2SGordon Ross 	if (related) {
1183a90cf9f2SGordon Ross 		if (sr->fid_ofile == NULL)
1184a90cf9f2SGordon Ross 			return (NT_STATUS_INVALID_PARAMETER);
1185a90cf9f2SGordon Ross 		sr->smb_fid = sr->fid_ofile->f_fid;
1186a90cf9f2SGordon Ross 		return (0);
1187a90cf9f2SGordon Ross 	}
1188a90cf9f2SGordon Ross 
1189a90cf9f2SGordon Ross 	/*
1190a90cf9f2SGordon Ross 	 * If we could be sure this is called only once per cmd,
1191a90cf9f2SGordon Ross 	 * we could simply ASSERT(sr->fid_ofile == NULL) here.
1192a90cf9f2SGordon Ross 	 * However, there are cases where it can be called again
1193a90cf9f2SGordon Ross 	 * handling the same command, so let's tolerate that.
1194a90cf9f2SGordon Ross 	 */
1195a90cf9f2SGordon Ross 	if (sr->fid_ofile == NULL) {
1196a90cf9f2SGordon Ross 		sr->smb_fid = (uint16_t)fid->temporal;
1197a90cf9f2SGordon Ross 		sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid);
1198a90cf9f2SGordon Ross 	}
1199a90cf9f2SGordon Ross 	if (sr->fid_ofile == NULL)
1200a90cf9f2SGordon Ross 		return (NT_STATUS_FILE_CLOSED);
1201a90cf9f2SGordon Ross 
1202a90cf9f2SGordon Ross 	return (0);
1203a90cf9f2SGordon Ross }
1204a90cf9f2SGordon Ross 
1205a90cf9f2SGordon Ross /*
1206a90cf9f2SGordon Ross  * smb2_dispatch_stats_init
1207a90cf9f2SGordon Ross  *
1208a90cf9f2SGordon Ross  * Initializes dispatch statistics for SMB2.
1209a90cf9f2SGordon Ross  * See also smb_dispatch_stats_init(), which fills in
1210a90cf9f2SGordon Ross  * the lower part of the statistics array, from zero
1211a90cf9f2SGordon Ross  * through SMB_COM_NUM;
1212a90cf9f2SGordon Ross  */
1213a90cf9f2SGordon Ross void
1214a90cf9f2SGordon Ross smb2_dispatch_stats_init(smb_server_t *sv)
1215a90cf9f2SGordon Ross {
1216a90cf9f2SGordon Ross 	smb_disp_stats_t *sds = sv->sv_disp_stats2;
1217a90cf9f2SGordon Ross 	smb_kstat_req_t *ksr;
1218a90cf9f2SGordon Ross 	int		i;
1219a90cf9f2SGordon Ross 
1220a90cf9f2SGordon Ross 	ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2;
1221a90cf9f2SGordon Ross 
1222a90cf9f2SGordon Ross 	for (i = 0; i < SMB2__NCMDS; i++, ksr++) {
1223a90cf9f2SGordon Ross 		smb_latency_init(&sds[i].sdt_lat);
1224a90cf9f2SGordon Ross 		(void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name,
1225a90cf9f2SGordon Ross 		    sizeof (ksr->kr_name));
1226a90cf9f2SGordon Ross 	}
1227a90cf9f2SGordon Ross }
1228a90cf9f2SGordon Ross 
1229a90cf9f2SGordon Ross /*
1230a90cf9f2SGordon Ross  * smb2_dispatch_stats_fini
1231a90cf9f2SGordon Ross  *
1232a90cf9f2SGordon Ross  * Frees and destroyes the resources used for statistics.
1233a90cf9f2SGordon Ross  */
1234a90cf9f2SGordon Ross void
1235a90cf9f2SGordon Ross smb2_dispatch_stats_fini(smb_server_t *sv)
1236a90cf9f2SGordon Ross {
1237a90cf9f2SGordon Ross 	smb_disp_stats_t *sds = sv->sv_disp_stats2;
1238a90cf9f2SGordon Ross 	int	i;
1239a90cf9f2SGordon Ross 
1240a90cf9f2SGordon Ross 	for (i = 0; i < SMB2__NCMDS; i++)
1241a90cf9f2SGordon Ross 		smb_latency_destroy(&sds[i].sdt_lat);
1242a90cf9f2SGordon Ross }
1243a90cf9f2SGordon Ross 
1244a90cf9f2SGordon Ross void
1245a90cf9f2SGordon Ross smb2_dispatch_stats_update(smb_server_t *sv,
1246a90cf9f2SGordon Ross     smb_kstat_req_t *ksr, int first, int nreq)
1247a90cf9f2SGordon Ross {
1248a90cf9f2SGordon Ross 	smb_disp_stats_t *sds = sv->sv_disp_stats2;
1249a90cf9f2SGordon Ross 	int	i;
1250a90cf9f2SGordon Ross 	int	last;
1251a90cf9f2SGordon Ross 
1252a90cf9f2SGordon Ross 	last = first + nreq - 1;
1253a90cf9f2SGordon Ross 
1254a90cf9f2SGordon Ross 	if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS))  {
1255a90cf9f2SGordon Ross 		for (i = first; i <= last; i++, ksr++) {
1256a90cf9f2SGordon Ross 			ksr->kr_rxb = sds[i].sdt_rxb;
1257a90cf9f2SGordon Ross 			ksr->kr_txb = sds[i].sdt_txb;
1258a90cf9f2SGordon Ross 			mutex_enter(&sds[i].sdt_lat.ly_mutex);
1259a90cf9f2SGordon Ross 			ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq;
1260a90cf9f2SGordon Ross 			ksr->kr_sum = sds[i].sdt_lat.ly_a_sum;
1261a90cf9f2SGordon Ross 			ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean;
1262a90cf9f2SGordon Ross 			ksr->kr_a_stddev =
1263a90cf9f2SGordon Ross 			    sds[i].sdt_lat.ly_a_stddev;
1264a90cf9f2SGordon Ross 			ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean;
1265a90cf9f2SGordon Ross 			ksr->kr_d_stddev =
1266a90cf9f2SGordon Ross 			    sds[i].sdt_lat.ly_d_stddev;
1267a90cf9f2SGordon Ross 			sds[i].sdt_lat.ly_d_mean = 0;
1268a90cf9f2SGordon Ross 			sds[i].sdt_lat.ly_d_nreq = 0;
1269a90cf9f2SGordon Ross 			sds[i].sdt_lat.ly_d_stddev = 0;
1270a90cf9f2SGordon Ross 			sds[i].sdt_lat.ly_d_sum = 0;
1271a90cf9f2SGordon Ross 			mutex_exit(&sds[i].sdt_lat.ly_mutex);
1272a90cf9f2SGordon Ross 		}
1273a90cf9f2SGordon Ross 	}
1274a90cf9f2SGordon Ross }
1275