[PATCH] smbXcli_base plumbing and torture tests for SMB2 credits

Ralph Böhme slow at samba.org
Fri Mar 3 15:20:34 UTC 2017


Hi!

I thought it would be nice to have some tests for SMB2 creditting, so I started
to work on this.

As a prerequisite I had to do some plumbing through the SMB2 client layers with
the goal of giving smbtorture control over the initial credits.

A few tests then use this plumbing to verify Windows 2016 Server creditting
behaviour. It grants up to 8192 credits by default, so the tests are marked
failing against Samba as we only grant a maximum of 512.

I discussed with metze whether we might want to raise our limit, but we don't
see a need, as even with conservative SMB2 IO RTT of 5 ms the bandwith-delay
product for 10 (!) Gbit ethernet is

SMB2 BDP = 5 ms * 10 GBit/s = 6.25 MB

6.25 MB / (64 KB / credit) ~= 98 credits

So with 98 credits granted the client's mid window is already large enough to
saturate the link. 512 should give enough room for higher average latency or
hiccups. Once we get to 100 Gbit ethernet in real world we'll have to revisit
this.

Cheerio!
-slow
-------------- next part --------------
From 1fb27bf5c248d71373f28f7781927ff806c1d8bb Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Mon, 27 Feb 2017 16:14:39 +0100
Subject: [PATCH 1/5] libcli/smb: add max_credits arg to smbXcli_negprot_send()

This allows source4/torture code to set the option for tests by
preparing a struct smbcli_options with max_credits set to some value and
pass that to a torture_smb2_connection_ext().

This will be used in subsequent smbtorture test for SMB2 creditting.

Behaviour of existing upper layers is unchanged, they simply pass the
wanted max credits value to smbXcli_negprot_send() instead of
retrofitting it with a call to smb2cli_conn_set_max_credits().

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 libcli/smb/smbXcli_base.c         | 20 ++++++++------------
 libcli/smb/smbXcli_base.h         |  3 ++-
 source3/libsmb/cliconnect.c       |  4 +++-
 source3/torture/torture.c         |  2 +-
 source4/libcli/raw/libcliraw.h    |  1 +
 source4/libcli/raw/rawnegotiate.c |  3 ++-
 source4/libcli/smb2/connect.c     |  6 ++----
 source4/param/loadparm.c          |  2 ++
 8 files changed, 21 insertions(+), 20 deletions(-)

diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c
index a7b24f0..70285e6 100644
--- a/libcli/smb/smbXcli_base.c
+++ b/libcli/smb/smbXcli_base.c
@@ -4092,7 +4092,8 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
 					struct smbXcli_conn *conn,
 					uint32_t timeout_msec,
 					enum protocol_types min_protocol,
-					enum protocol_types max_protocol)
+					enum protocol_types max_protocol,
+					uint16_t max_credits)
 {
 	struct tevent_req *req, *subreq;
 	struct smbXcli_negprot_state *state;
@@ -4125,6 +4126,10 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
 	conn->max_protocol = max_protocol;
 	conn->protocol = PROTOCOL_NONE;
 
+	if (max_protocol >= PROTOCOL_SMB2_02) {
+		conn->smb2.max_credits = max_credits;
+	}
+
 	if ((min_protocol < PROTOCOL_SMB2_02) &&
 	    (max_protocol < PROTOCOL_SMB2_02)) {
 		/*
@@ -4147,16 +4152,6 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
 		 */
 		conn->dispatch_incoming = smb2cli_conn_dispatch_incoming;
 
-		/*
-		 * As we're starting with an SMB2 negprot, emulate Windows
-		 * and ask for 31 credits in the initial SMB2 negprot.
-		 * If we don't and leave requested credits at
-		 * zero, MacOSX servers return zero credits on
-		 * the negprot reply and we fail to connect.
-		 */
-		smb2cli_conn_set_max_credits(conn,
-			WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
-
 		subreq = smbXcli_negprot_smb2_subreq(state);
 		if (tevent_req_nomem(subreq, req)) {
 			return tevent_req_post(req, ev);
@@ -5137,7 +5132,8 @@ NTSTATUS smbXcli_negprot(struct smbXcli_conn *conn,
 		goto fail;
 	}
 	req = smbXcli_negprot_send(frame, ev, conn, timeout_msec,
-				   min_protocol, max_protocol);
+				   min_protocol, max_protocol,
+				   WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
 	if (req == NULL) {
 		goto fail;
 	}
diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h
index 702eedf..6041ae5 100644
--- a/libcli/smb/smbXcli_base.h
+++ b/libcli/smb/smbXcli_base.h
@@ -436,7 +436,8 @@ struct tevent_req *smbXcli_negprot_send(TALLOC_CTX *mem_ctx,
 					struct smbXcli_conn *conn,
 					uint32_t timeout_msec,
 					enum protocol_types min_protocol,
-					enum protocol_types max_protocol);
+					enum protocol_types max_protocol,
+					uint16_t max_credits);
 NTSTATUS smbXcli_negprot_recv(struct tevent_req *req);
 NTSTATUS smbXcli_negprot(struct smbXcli_conn *conn,
 			 uint32_t timeout_msec,
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index a9451fb..029c3d4 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -39,6 +39,7 @@
 #include "../libcli/smb/smbXcli_base.h"
 #include "../libcli/smb/smb_seal.h"
 #include "lib/param/param.h"
+#include "../libcli/smb/smb2_negotiate_context.h"
 
 #define STAR_SMBSERVER "*SMBSERVER"
 
@@ -2754,7 +2755,8 @@ static void cli_start_connection_connected(struct tevent_req *subreq)
 	subreq = smbXcli_negprot_send(state, state->ev, state->cli->conn,
 				      state->cli->timeout,
 				      state->min_protocol,
-				      state->max_protocol);
+				      state->max_protocol,
+				      WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
index 3062122..4e6576c 100644
--- a/source3/torture/torture.c
+++ b/source3/torture/torture.c
@@ -2990,7 +2990,7 @@ static bool run_negprot_nowait(int dummy)
 		struct tevent_req *req;
 
 		req = smbXcli_negprot_send(ev, ev, cli->conn, cli->timeout,
-					   PROTOCOL_CORE, PROTOCOL_NT1);
+					   PROTOCOL_CORE, PROTOCOL_NT1, 0);
 		if (req == NULL) {
 			TALLOC_FREE(ev);
 			return false;
diff --git a/source4/libcli/raw/libcliraw.h b/source4/libcli/raw/libcliraw.h
index 8220cd7..96dfcd4 100644
--- a/source4/libcli/raw/libcliraw.h
+++ b/source4/libcli/raw/libcliraw.h
@@ -103,6 +103,7 @@ struct smbcli_options {
 	enum smb_signing_setting signing;
 	uint32_t smb2_capabilities;
 	struct GUID client_guid;
+	uint64_t max_credits;
 };
 
 /* this is the context for the client transport layer */
diff --git a/source4/libcli/raw/rawnegotiate.c b/source4/libcli/raw/rawnegotiate.c
index 4b42c26..f6a189f 100644
--- a/source4/libcli/raw/rawnegotiate.c
+++ b/source4/libcli/raw/rawnegotiate.c
@@ -60,7 +60,8 @@ struct tevent_req *smb_raw_negotiate_send(TALLOC_CTX *mem_ctx,
 				      transport->conn,
 				      timeout_msec,
 				      minprotocol,
-				      maxprotocol);
+				      maxprotocol,
+				      transport->options.max_credits);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c
index 1a6ae34..8ff56c9 100644
--- a/source4/libcli/smb2/connect.c
+++ b/source4/libcli/smb2/connect.c
@@ -155,7 +155,8 @@ static void smb2_connect_socket_done(struct composite_context *creq)
 	subreq = smbXcli_negprot_send(state, state->ev,
 				      state->transport->conn, timeout_msec,
 				      min_protocol,
-				      state->transport->options.max_protocol);
+				      state->transport->options.max_protocol,
+				      state->transport->options.max_credits);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
@@ -181,9 +182,6 @@ static void smb2_connect_negprot_done(struct tevent_req *subreq)
 		return;
 	}
 
-	/* This is a hack... */
-	smb2cli_conn_set_max_credits(transport->conn, 30);
-
 	state->session = smb2_session_init(transport, state->gensec_settings, state);
 	if (tevent_req_nomem(state->session, req)) {
 		return;
diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c
index f53b2dd..1044a07 100644
--- a/source4/param/loadparm.c
+++ b/source4/param/loadparm.c
@@ -30,6 +30,7 @@
 #include "lib/param/param.h"
 #include "libcli/raw/libcliraw.h"
 #include "librpc/ndr/libndr.h"
+#include "libcli/smb/smb2_negotiate_context.h"
 
 void lpcfg_smbcli_options(struct loadparm_context *lp_ctx,
 			 struct smbcli_options *options)
@@ -47,6 +48,7 @@ void lpcfg_smbcli_options(struct loadparm_context *lp_ctx,
 	options->use_level2_oplocks = true;
 	options->smb2_capabilities = SMB2_CAP_ALL;
 	options->client_guid = GUID_random();
+	options->max_credits = WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK;
 }
 
 void lpcfg_smbcli_session_options(struct loadparm_context *lp_ctx,
-- 
2.9.3


From 5384cb6a6e253375dcaac77a0d8f4a0d112626e0 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Mon, 27 Feb 2017 12:29:25 +0100
Subject: [PATCH 2/5] libcli/smb: add smb2cli_conn_get_cur_credits

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 libcli/smb/smbXcli_base.c | 5 +++++
 libcli/smb/smbXcli_base.h | 1 +
 2 files changed, 6 insertions(+)

diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c
index 70285e6..0b83a13 100644
--- a/libcli/smb/smbXcli_base.c
+++ b/libcli/smb/smbXcli_base.c
@@ -2782,6 +2782,11 @@ void smb2cli_conn_set_max_credits(struct smbXcli_conn *conn,
 	conn->smb2.max_credits = max_credits;
 }
 
+uint16_t smb2cli_conn_get_cur_credits(struct smbXcli_conn *conn)
+{
+	return conn->smb2.cur_credits;
+}
+
 uint8_t smb2cli_conn_get_io_priority(struct smbXcli_conn *conn)
 {
 	if (conn->protocol < PROTOCOL_SMB3_11) {
diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h
index 6041ae5..12dd7de 100644
--- a/libcli/smb/smbXcli_base.h
+++ b/libcli/smb/smbXcli_base.h
@@ -368,6 +368,7 @@ uint32_t smb2cli_conn_max_read_size(struct smbXcli_conn *conn);
 uint32_t smb2cli_conn_max_write_size(struct smbXcli_conn *conn);
 void smb2cli_conn_set_max_credits(struct smbXcli_conn *conn,
 				  uint16_t max_credits);
+uint16_t smb2cli_conn_get_cur_credits(struct smbXcli_conn *conn);
 uint8_t smb2cli_conn_get_io_priority(struct smbXcli_conn *conn);
 void smb2cli_conn_set_io_priority(struct smbXcli_conn *conn,
 				  uint8_t io_priority);
-- 
2.9.3


From a8e6b39ef284d4f67d75d768f631738e3695e222 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Mon, 27 Feb 2017 07:12:09 +0100
Subject: [PATCH 3/5] s4/torture: add some SMB2 crediting tests

These tests verify that a server grants at least 8192 credits in a
successfull session setup and in a single SMB2 request. Both tests pass
against Windows 2016 Server but currently fail against Samba.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 selftest/knownfail                 |   2 +
 source4/torture/smb2/credits.c     | 162 +++++++++++++++++++++++++++++++++++++
 source4/torture/smb2/smb2.c        |   1 +
 source4/torture/smb2/wscript_build |   1 +
 4 files changed, 166 insertions(+)
 create mode 100644 source4/torture/smb2/credits.c

diff --git a/selftest/knownfail b/selftest/knownfail
index d96e238..a5e2dd4 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -312,3 +312,5 @@
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_add_duplicate_different_type.*
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_rank_none.*
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_security_descriptor.*
+^samba3.smb2.credits.session_setup_credits_granted.*
+^samba3.smb2.credits.single_req_credits_granted.*
diff --git a/source4/torture/smb2/credits.c b/source4/torture/smb2/credits.c
new file mode 100644
index 0000000..d34b1d5
--- /dev/null
+++ b/source4/torture/smb2/credits.c
@@ -0,0 +1,162 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   test suite for SMB2 credits
+
+   Copyright (C) Ralph Boehme 2017
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "lib/param/param.h"
+
+/**
+ * Request 64k credits in negprot/sessionsetup and require at least 8k
+ *
+ * This passes against Windows 2016
+ **/
+static bool test_session_setup_credits_granted(struct torture_context *tctx,
+					       struct smb2_tree *_tree)
+{
+	struct smbcli_options options;
+	struct smb2_transport *transport = NULL;
+	struct smb2_tree *tree = NULL;
+	uint16_t cur_credits;
+	NTSTATUS status;
+	bool ret = true;
+
+	transport = _tree->session->transport;
+	options = transport->options;
+
+	status = smb2_logoff(_tree->session);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_logoff failed\n");
+	TALLOC_FREE(_tree);
+
+	options.max_credits = 65535;
+
+	ret = torture_smb2_connection_ext(tctx, 0, &options, &tree);
+	torture_assert_goto(tctx, ret == true, ret, done,
+			    "torture_smb2_connection_ext failed\n");
+
+	transport = tree->session->transport;
+
+	cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
+	if (cur_credits < 8192) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "Server only granted %" PRIu16" credits\n",
+			       cur_credits);
+		ret = false;
+		goto done;
+	}
+
+done:
+	TALLOC_FREE(tree);
+	return ret;
+}
+
+/**
+ * Request 64K credits in a single SMB2 request and requite at least 8192
+ *
+ * This passes against Windows 2016
+ **/
+static bool test_single_req_credits_granted(struct torture_context *tctx,
+					    struct smb2_tree *_tree)
+{
+	struct smbcli_options options;
+	struct smb2_transport *transport = NULL;
+	struct smb2_tree *tree = NULL;
+	struct smb2_handle h = {{0}};
+	struct smb2_create create;
+	const char *fname = "single_req_credits_granted.dat";
+	uint16_t cur_credits;
+	NTSTATUS status;
+	bool ret = true;
+
+	smb2_util_unlink(_tree, fname);
+
+	transport = _tree->session->transport;
+	options = transport->options;
+
+	status = smb2_logoff(_tree->session);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_logoff failed\n");
+	TALLOC_FREE(_tree);
+
+	options.max_credits = 1;
+
+	ret = torture_smb2_connection_ext(tctx, 0, &options, &tree);
+	torture_assert_goto(tctx, ret == true, ret, done,
+			    "torture_smb2_connection_ext failed\n");
+
+	transport = tree->session->transport;
+
+	cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
+	if (cur_credits != 1) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "Only wanted 1 credit but server granted %" PRIu16"\n",
+			       cur_credits);
+		ret = false;
+		goto done;
+	}
+
+	smb2cli_conn_set_max_credits(transport->conn, 65535);
+
+	ZERO_STRUCT(create);
+	create.in.impersonation_level	= NTCREATEX_IMPERSONATION_IMPERSONATION;
+	create.in.desired_access	= SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes	= FILE_ATTRIBUTE_NORMAL;
+	create.in.create_disposition	= NTCREATEX_DISP_OPEN_IF;
+	create.in.fname			= fname;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_create failed\n");
+	h = create.out.file.handle;
+
+	cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
+	if (cur_credits < 8192) {
+		torture_result(tctx, TORTURE_FAIL,
+			       "Server only granted %" PRIu16" credits\n",
+			       cur_credits);
+		ret = false;
+		goto done;
+	}
+
+done:
+	if (!smb2_util_handle_empty(h)) {
+		smb2_util_close(tree, h);
+	}
+	smb2_util_unlink(tree, fname);
+	TALLOC_FREE(tree);
+	return ret;
+}
+
+struct torture_suite *torture_smb2_crediting_init(void)
+{
+	struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "credits");
+
+	torture_suite_add_1smb2_test(suite, "session_setup_credits_granted", test_session_setup_credits_granted);
+	torture_suite_add_1smb2_test(suite, "single_req_credits_granted", test_single_req_credits_granted);
+
+	suite->description = talloc_strdup(suite, "SMB2-CREDITS tests");
+
+	return suite;
+}
diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c
index ca07992..9f0f6b3 100644
--- a/source4/torture/smb2/smb2.c
+++ b/source4/torture/smb2/smb2.c
@@ -172,6 +172,7 @@ NTSTATUS torture_smb2_init(void)
 	torture_suite_add_suite(suite, torture_smb2_replay_init());
 	torture_suite_add_simple_test(suite, "dosmode", torture_smb2_dosmode);
 	torture_suite_add_simple_test(suite, "maxfid", torture_smb2_maxfid);
+	torture_suite_add_suite(suite, torture_smb2_crediting_init());
 
 	torture_suite_add_suite(suite, torture_smb2_doc_init());
 
diff --git a/source4/torture/smb2/wscript_build b/source4/torture/smb2/wscript_build
index ba7a9bd..e26ac30 100644
--- a/source4/torture/smb2/wscript_build
+++ b/source4/torture/smb2/wscript_build
@@ -6,6 +6,7 @@ bld.SAMBA_MODULE('TORTURE_SMB2',
         compound.c
         connect.c
         create.c
+        credits.c
         delete-on-close.c
         dir.c
         dosmode.c
-- 
2.9.3


From 91f96f6d2ea17e83f9d4eca48ef98a5f23a76b46 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sun, 26 Feb 2017 09:28:12 +0100
Subject: [PATCH 4/5] libcli/smb: add smb2cli_conn_get_mid and
 smb2cli_conn_set_mid

This will be needed for a torture test in the next commit.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 libcli/smb/smbXcli_base.c | 10 ++++++++++
 libcli/smb/smbXcli_base.h |  2 ++
 2 files changed, 12 insertions(+)

diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c
index 0b83a13..9521fc6 100644
--- a/libcli/smb/smbXcli_base.c
+++ b/libcli/smb/smbXcli_base.c
@@ -6248,3 +6248,13 @@ bool smb2cli_tcon_is_encryption_on(struct smbXcli_tcon *tcon)
 {
 	return tcon->smb2.should_encrypt;
 }
+
+void smb2cli_conn_set_mid(struct smbXcli_conn *conn, uint64_t mid)
+{
+	conn->smb2.mid = mid;
+}
+
+uint64_t smb2cli_conn_get_mid(struct smbXcli_conn *conn)
+{
+	return conn->smb2.mid;
+}
diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h
index 12dd7de..84f4a6b 100644
--- a/libcli/smb/smbXcli_base.h
+++ b/libcli/smb/smbXcli_base.h
@@ -378,6 +378,8 @@ void smb2cli_conn_set_cc_chunk_len(struct smbXcli_conn *conn,
 uint32_t smb2cli_conn_cc_max_chunks(struct smbXcli_conn *conn);
 void smb2cli_conn_set_cc_max_chunks(struct smbXcli_conn *conn,
 				    uint32_t max_chunks);
+void smb2cli_conn_set_mid(struct smbXcli_conn *conn, uint64_t mid);
+uint64_t smb2cli_conn_get_mid(struct smbXcli_conn *conn);
 
 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
 				      struct tevent_context *ev,
-- 
2.9.3


From 0c19edce3d72f5d089f09e3da9893ed933d2ea3a Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Mon, 27 Feb 2017 12:55:04 +0100
Subject: [PATCH 5/5] s4/torture: add a creditting test skipping a SMB2 MID

This tests that skipping a SMB2 MID the client's usable MID window is

[unused mid, unused mid + 8192]

The test currently fails against Samba as we only grant up to 512
credits. It passes against Windows 2016 as that grants up to 8192
credits by default.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 selftest/knownfail             |   1 +
 source4/selftest/tests.py      |   2 +-
 source4/torture/smb2/credits.c | 105 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+), 1 deletion(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index a5e2dd4..7c5417b 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -314,3 +314,4 @@
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_security_descriptor.*
 ^samba3.smb2.credits.session_setup_credits_granted.*
 ^samba3.smb2.credits.single_req_credits_granted.*
+^samba3.smb2.credits.skipped_mid.*
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index 83eff52..d52c873 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -303,7 +303,7 @@ for t in nbt_tests:
 ntvfsargs = ["--option=torture:sharedelay=100000", "--option=torture:oplocktimeout=3", "--option=torture:writetimeupdatedelay=500000"]
 
 # Filter smb2 tests that should not run against ad_dc_ntvfs
-smb2_s3only = ["smb2.change_notify_disabled", "smb2.dosmode"]
+smb2_s3only = ["smb2.change_notify_disabled", "smb2.dosmode", "smb2.credits"]
 smb2 = [x for x in smbtorture4_testsuites("smb2.") if x not in smb2_s3only]
 
 #The QFILEINFO-IPC test needs to be on ipc$
diff --git a/source4/torture/smb2/credits.c b/source4/torture/smb2/credits.c
index d34b1d5..5aa3586 100644
--- a/source4/torture/smb2/credits.c
+++ b/source4/torture/smb2/credits.c
@@ -149,12 +149,117 @@ done:
 	return ret;
 }
 
+static bool test_crediting_skipped_mid(struct torture_context *tctx,
+				       struct smb2_tree *_tree)
+{
+	struct smbcli_options options;
+	struct smb2_transport *transport = NULL;
+	struct smb2_tree *tree = NULL;
+	struct smb2_handle h = {{0}};
+	struct smb2_create create;
+	const char *fname = "skipped_mid.dat";
+	uint64_t mid;
+	uint16_t cur_credits;
+	NTSTATUS status;
+	bool ret = true;
+
+	smb2_util_unlink(_tree, fname);
+
+	transport = _tree->session->transport;
+	options = transport->options;
+
+	status = smb2_logoff(_tree->session);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_logoff failed\n");
+	TALLOC_FREE(_tree);
+
+	options.max_credits = 8192;
+
+	ret = torture_smb2_connection_ext(tctx, 0, &options, &tree);
+	torture_assert_goto(tctx, ret == true, ret, done, "torture_smb2_connection_ext failed\n");
+
+	transport = tree->session->transport;
+
+	cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
+	if (cur_credits != 8192) {
+		torture_result(tctx, TORTURE_FAIL, "Server only granted %" PRIu16" credits\n", cur_credits);
+		ret = false;
+		goto done;
+	}
+
+	ZERO_STRUCT(create);
+	create.in.impersonation_level	= NTCREATEX_IMPERSONATION_IMPERSONATION;
+	create.in.desired_access	= SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes	= FILE_ATTRIBUTE_NORMAL;
+	create.in.create_disposition	= NTCREATEX_DISP_OPEN_IF;
+	create.in.fname			= fname;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
+	h = create.out.file.handle;
+
+	/*
+	 * See what happens if we skip a mid. As we want to avoid triggering our
+	 * client side mid window check we keep conn->smb2.cur_credits
+	 * unchanged so the server keeps granting credits until it's max mid
+	 * windows size is reached at which point it will disconnect us:
+	 *
+	 * o Windows 2016 currently has a maximum mid window size of 8192 by
+	 *   default
+	 *
+	 * o Samba's limit is 512
+	 *
+	 * o Windows 2008r2 uses some special algorithm (MS-SMB2 3.3.1.1
+	 *   footnote <167>) that kicks in once a mid is skipped, resulting in a
+	 *   maximum window size of 100-300 depending on the number of granted
+	 *   credits at the moment of skipping a mid.
+	 */
+
+	mid = smb2cli_conn_get_mid(tree->session->transport->conn);
+	smb2cli_conn_set_mid(tree->session->transport->conn, mid + 1);
+
+	for (int i = 0; i < 8191; i++) {
+		status = smb2_util_write(tree, h, "\0", 0, 1);
+		if (!NT_STATUS_IS_OK(status)) {
+			torture_result(tctx, TORTURE_FAIL, "Server only allowed %d writes\n", i);
+			ret = false;
+			goto done;
+		}
+	}
+
+	/*
+	 * Now use the skipped mid (the smb2_util_close...), we should
+	 * immediately get a full mid window of size 8192.
+	 */
+	smb2cli_conn_set_mid(tree->session->transport->conn, mid);
+	status = smb2_util_close(tree, h);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_close failed\n");
+	ZERO_STRUCT(h);
+
+	cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
+	if (cur_credits != 8192) {
+		torture_result(tctx, TORTURE_FAIL, "Server only granted %" PRIu16" credits\n", cur_credits);
+		ret = false;
+		goto done;
+	}
+
+	smb2cli_conn_set_mid(tree->session->transport->conn, mid + 8192);
+
+done:
+	if (!smb2_util_handle_empty(h)) {
+		smb2_util_close(tree, h);
+	}
+	smb2_util_unlink(tree, fname);
+	TALLOC_FREE(tree);
+	return ret;
+}
+
 struct torture_suite *torture_smb2_crediting_init(void)
 {
 	struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "credits");
 
 	torture_suite_add_1smb2_test(suite, "session_setup_credits_granted", test_session_setup_credits_granted);
 	torture_suite_add_1smb2_test(suite, "single_req_credits_granted", test_single_req_credits_granted);
+	torture_suite_add_1smb2_test(suite, "skipped_mid", test_crediting_skipped_mid);
 
 	suite->description = talloc_strdup(suite, "SMB2-CREDITS tests");
 
-- 
2.9.3



More information about the samba-technical mailing list