Add multichannel tests to smbtorture

Sachin Prabhu sprabhu at redhat.com
Wed Mar 27 17:07:36 UTC 2019


A set of 19 patches which test the server for various simulated
multichannel related workloads and a major portion includes testing
for oplock/lease break when encountering network interruptions.

We use two methods to simulate a network interruption for individual channels.
a) iptables - Uses iptables to block the channel. This requires
privileged access and can only be run on linux based box with iptable
support. Called with argument --option=torture:use_iptables=true
b) the ignore handler - In this case, the client(torture script)
doesn't respond to oplock/lease break commands from the server.
By default we use the ignore handler.

The patches we are testing on the samba server waits for responses to
the break requests from the samba servers to confirm that the break
commands were received by them and acted on. This works with the
ignore handler we described earlier. This is therefore the default
method and can be run in automated, non privileged environments to
test the retry behaviour of the samba server.

The Windows based SMB servers OTOH rely on the tcp stack to confirm
delivery of the break request and therefore need the iptables method.
I have been using this to manually test the behaviour of windows
servers.

The work on these tests were initially started by Gunther Descher. I
have since picked these up and expanded on them.

Sachin Prabhu
-------------- next part --------------
From 1f28eec16e6b9fbfe8d7bd26e92cab2774fea2d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Wed, 20 Sep 2017 19:19:37 +0200
Subject: [PATCH 01/19] s4-torture: include torture/util.h in lease break
 handler
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Guenther

Signed-off-by: Günther Deschner <gd at samba.org>
---
 source4/torture/smb2/lease_break_handler.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source4/torture/smb2/lease_break_handler.h b/source4/torture/smb2/lease_break_handler.h
index 54e615c3082..dc8841bb8a1 100644
--- a/source4/torture/smb2/lease_break_handler.h
+++ b/source4/torture/smb2/lease_break_handler.h
@@ -19,6 +19,8 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include "torture/util.h"
+
 struct lease_break_info {
 	struct torture_context *tctx;
 
-- 
2.20.1


From fb0af4f0f9ab8e1d7bb395029c9941be3af7611b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Tue, 19 Jan 2016 14:55:03 +0100
Subject: [PATCH 02/19] s4-torture: add new smb2 multichannel suite skeleton.

Also Skip MC tests for s4 ntvfs fileserver, it's not supported at all.
Use knownfail for s3 fileserver for the time being (until socketwrapper
supports fd-passing).

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 selftest/knownfail                  |  1 +
 selftest/skip                       |  1 +
 source4/torture/smb2/multichannel.c | 56 +++++++++++++++++++++++++++++
 source4/torture/smb2/smb2.c         |  1 +
 source4/torture/smb2/wscript_build  |  1 +
 5 files changed, 60 insertions(+)
 create mode 100644 source4/torture/smb2/multichannel.c

diff --git a/selftest/knownfail b/selftest/knownfail
index 16c2274daec..f8346f2917c 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -192,6 +192,7 @@
 ^samba3.smb2.lock.*replay
 ^samba3.smb2.lease.statopen3
 ^samba3.smb2.lease.unlink # we currently do not downgrade RH lease to R after unlink
+^samba3.smb2.multichannel
 ^samba4.smb2.ioctl.compress_notsup.*\(ad_dc_ntvfs\)
 ^samba3.raw.session.*reauth2 # maybe fix this?
 ^samba3.rpc.lsa.secrets.seal # This gives NT_STATUS_LOCAL_USER_SESSION_KEY
diff --git a/selftest/skip b/selftest/skip
index f59f824147c..367d56fadce 100644
--- a/selftest/skip
+++ b/selftest/skip
@@ -92,6 +92,7 @@
 ^samba4.smb2.dir
 ^samba4.smb2.session
 ^samba4.smb2.compound
+^samba4.smb2.multichannel
 ^samba4.smb2.oplock.levelii501		# No test yet
 # SMB2 in s4 does not seem to support rename correctly
 ^samba4.smb2.rename.*\(ad_dc_ntvfs\)$
diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
new file mode 100644
index 00000000000..2c72bd779d5
--- /dev/null
+++ b/source4/torture/smb2/multichannel.c
@@ -0,0 +1,56 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * test SMB2 multichannel operations
+ *
+ * Copyright (C) Guenther Deschner, 2016
+ *
+ * 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/util.h"
+#include "torture/smb2/proto.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+#define CHECK_STATUS(status, correct) do { \
+	if (!NT_STATUS_EQUAL(status, correct)) { \
+		torture_result(tctx, TORTURE_FAIL, \
+			"(%s) Incorrect status %s - should be %s\n", \
+			 __location__, nt_errstr(status), nt_errstr(correct)); \
+		return false; \
+	} } while (0)
+
+static bool test_session_bind(struct torture_context *tctx,
+			      struct smb2_tree *tree)
+{
+	/* TODO */
+
+	return true;
+}
+
+struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
+{
+	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
+
+	torture_suite_add_1smb2_test(suite, "session-bind", test_session_bind);
+
+	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
+
+	return suite;
+}
diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c
index a835dc7c050..2a465a55ba2 100644
--- a/source4/torture/smb2/smb2.c
+++ b/source4/torture/smb2/smb2.c
@@ -190,6 +190,7 @@ NTSTATUS torture_smb2_init(TALLOC_CTX *ctx)
 	torture_suite_add_suite(suite, torture_smb2_crediting_init(suite));
 
 	torture_suite_add_suite(suite, torture_smb2_doc_init(suite));
+	torture_suite_add_suite(suite, torture_smb2_multichannel_init(suite));
 
 	suite->description = talloc_strdup(suite, "SMB2-specific tests");
 
diff --git a/source4/torture/smb2/wscript_build b/source4/torture/smb2/wscript_build
index 8b0060a2831..e10d3d90ba5 100644
--- a/source4/torture/smb2/wscript_build
+++ b/source4/torture/smb2/wscript_build
@@ -19,6 +19,7 @@ bld.SAMBA_MODULE('TORTURE_SMB2',
         lock.c
         maxfid.c
         maxwrite.c
+        multichannel.c
         notify.c
         notify_disabled.c
         oplock.c
-- 
2.20.1


From f40dec5a4fb05776e6bb2eac42c5dddf6e0f2415 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Wed, 28 Sep 2016 21:23:20 +0200
Subject: [PATCH 03/19] s4-torture: move oplock break handler out of the replay
 testsuite.

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/oplock_break_handler.c | 91 +++++++++++++++++++++
 source4/torture/smb2/oplock_break_handler.h | 46 +++++++++++
 source4/torture/smb2/replay.c               | 78 +-----------------
 source4/torture/smb2/wscript_build          |  1 +
 4 files changed, 139 insertions(+), 77 deletions(-)
 create mode 100644 source4/torture/smb2/oplock_break_handler.c
 create mode 100644 source4/torture/smb2/oplock_break_handler.h

diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c
new file mode 100644
index 00000000000..30863a9167e
--- /dev/null
+++ b/source4/torture/smb2/oplock_break_handler.c
@@ -0,0 +1,91 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * test suite for SMB2 replay
+ *
+ * Copyright (C) Anubhav Rakshit 2014
+ * Copyright (C) Stefan Metzmacher 2014
+ *
+ * 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 "oplock_break_handler.h"
+
+struct break_info break_info;
+
+static void torture_oplock_ack_callback(struct smb2_request *req)
+{
+	NTSTATUS status;
+
+	status = smb2_break_recv(req, &break_info.br);
+	if (!NT_STATUS_IS_OK(status)) {
+		break_info.failures++;
+		break_info.failure_status = status;
+	}
+}
+
+/**
+ * A general oplock break notification handler.  This should be used when a
+ * test expects to break from batch or exclusive to a lower level.
+ */
+
+bool torture_oplock_ack_handler(struct smb2_transport *transport,
+				const struct smb2_handle *handle,
+				uint8_t level,
+				void *private_data)
+{
+	struct smb2_tree *tree = private_data;
+	const char *name;
+	struct smb2_request *req;
+
+	ZERO_STRUCT(break_info.br);
+
+	break_info.handle	= *handle;
+	break_info.level	= level;
+	break_info.count++;
+
+	switch (level) {
+	case SMB2_OPLOCK_LEVEL_II:
+		name = "level II";
+		break;
+	case SMB2_OPLOCK_LEVEL_NONE:
+		name = "none";
+		break;
+	default:
+		name = "unknown";
+		break_info.failures++;
+	}
+	torture_comment(break_info.tctx,
+			"transport[%p] Acking to %s [0x%02X] in oplock handler\n",
+			transport, name, level);
+
+	break_info.br.in.file.handle	= *handle;
+	break_info.br.in.oplock_level	= level;
+	break_info.br.in.reserved	= 0;
+	break_info.br.in.reserved2	= 0;
+	break_info.received_transport = tree->session->transport;
+	SMB_ASSERT(tree->session->transport == transport);
+
+	req = smb2_break_send(tree, &break_info.br);
+	req->async.fn = torture_oplock_ack_callback;
+	req->async.private_data = NULL;
+
+	return true;
+}
diff --git a/source4/torture/smb2/oplock_break_handler.h b/source4/torture/smb2/oplock_break_handler.h
new file mode 100644
index 00000000000..f3216d6d095
--- /dev/null
+++ b/source4/torture/smb2/oplock_break_handler.h
@@ -0,0 +1,46 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * test suite for SMB2 replay
+ *
+ * Copyright (C) Anubhav Rakshit 2014
+ * Copyright (C) Stefan Metzmacher 2014
+ *
+ * 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/>.
+ */
+
+struct break_info {
+	struct torture_context *tctx;
+	struct smb2_handle handle;
+	uint8_t level;
+	struct smb2_break br;
+	int count;
+	int failures;
+	NTSTATUS failure_status;
+	struct smb2_transport *received_transport;
+};
+
+extern struct break_info break_info;
+
+bool torture_oplock_ack_handler(struct smb2_transport *transport,
+				const struct smb2_handle *handle,
+				uint8_t level,
+				void *private_data);
+
+static inline void torture_reset_break_info(struct torture_context *tctx,
+			      struct break_info *r)
+{
+	ZERO_STRUCTP(r);
+	r->tctx = tctx;
+}
diff --git a/source4/torture/smb2/replay.c b/source4/torture/smb2/replay.c
index 2ef40445aee..1085f2dd405 100644
--- a/source4/torture/smb2/replay.c
+++ b/source4/torture/smb2/replay.c
@@ -32,6 +32,7 @@
 #include "libcli/resolve/resolve.h"
 #include "lib/param/param.h"
 #include "lib/events/events.h"
+#include "oplock_break_handler.h"
 
 #define CHECK_VAL(v, correct) do { \
 	if ((v) != (correct)) { \
@@ -94,83 +95,6 @@
 
 #define BASEDIR "replaytestdir"
 
-struct break_info {
-	struct torture_context *tctx;
-	struct smb2_handle handle;
-	uint8_t level;
-	struct smb2_break br;
-	int count;
-	int failures;
-	NTSTATUS failure_status;
-};
-
-static struct break_info break_info;
-
-static void torture_reset_break_info(struct torture_context *tctx,
-				     struct break_info *r)
-{
-	ZERO_STRUCTP(r);
-	r->tctx = tctx;
-}
-
-static void torture_oplock_ack_callback(struct smb2_request *req)
-{
-	NTSTATUS status;
-
-	status = smb2_break_recv(req, &break_info.br);
-	if (!NT_STATUS_IS_OK(status)) {
-		break_info.failures++;
-		break_info.failure_status = status;
-	}
-
-	return;
-}
-
-/**
- * A general oplock break notification handler.  This should be used when a
- * test expects to break from batch or exclusive to a lower level.
- */
-static bool torture_oplock_ack_handler(struct smb2_transport *transport,
-				       const struct smb2_handle *handle,
-				       uint8_t level,
-				       void *private_data)
-{
-	struct smb2_tree *tree = private_data;
-	const char *name;
-	struct smb2_request *req;
-
-	ZERO_STRUCT(break_info.br);
-
-	break_info.handle	= *handle;
-	break_info.level	= level;
-	break_info.count++;
-
-	switch (level) {
-	case SMB2_OPLOCK_LEVEL_II:
-		name = "level II";
-		break;
-	case SMB2_OPLOCK_LEVEL_NONE:
-		name = "none";
-		break;
-	default:
-		name = "unknown";
-		break_info.failures++;
-	}
-	torture_comment(break_info.tctx,
-			"Acking to %s [0x%02X] in oplock handler\n",
-			name, level);
-
-	break_info.br.in.file.handle	= *handle;
-	break_info.br.in.oplock_level	= level;
-	break_info.br.in.reserved	= 0;
-	break_info.br.in.reserved2	= 0;
-
-	req = smb2_break_send(tree, &break_info.br);
-	req->async.fn = torture_oplock_ack_callback;
-	req->async.private_data = NULL;
-	return true;
-}
-
 /**
  * Timer handler function notifies the registering function that time is up
  */
diff --git a/source4/torture/smb2/wscript_build b/source4/torture/smb2/wscript_build
index e10d3d90ba5..1183cb93772 100644
--- a/source4/torture/smb2/wscript_build
+++ b/source4/torture/smb2/wscript_build
@@ -20,6 +20,7 @@ bld.SAMBA_MODULE('TORTURE_SMB2',
         maxfid.c
         maxwrite.c
         multichannel.c
+        oplock_break_handler.c
         notify.c
         notify_disabled.c
         oplock.c
-- 
2.20.1


From b99a8ace423945f87b11fbc7d055d828a1e4a614 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Thu, 14 Mar 2019 18:15:27 +0000
Subject: [PATCH 04/19] s4-torture: move torture_wait_for_oplock_break() to
 central oplock handler.

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/oplock.c               | 54 --------------------
 source4/torture/smb2/oplock_break_handler.c | 54 ++++++++++++++++++++
 source4/torture/smb2/oplock_break_handler.h |  1 +
 source4/torture/smb2/replay.c               | 55 ---------------------
 4 files changed, 55 insertions(+), 109 deletions(-)

diff --git a/source4/torture/smb2/oplock.c b/source4/torture/smb2/oplock.c
index 143078a37d1..435777bce0e 100644
--- a/source4/torture/smb2/oplock.c
+++ b/source4/torture/smb2/oplock.c
@@ -304,60 +304,6 @@ static bool open_smb2_connection_no_level2_oplocks(struct torture_context *tctx,
 	return true;
 }
 
-/*
-   Timer handler function notifies the registering function that time is up
-*/
-static void timeout_cb(struct tevent_context *ev,
-		       struct tevent_timer *te,
-		       struct timeval current_time,
-		       void *private_data)
-{
-	bool *timesup = (bool *)private_data;
-	*timesup = true;
-	return;
-}
-
-/*
-   Wait a short period of time to receive a single oplock break request
-*/
-static void torture_wait_for_oplock_break(struct torture_context *tctx)
-{
-	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
-	struct tevent_timer *te = NULL;
-	struct timeval ne;
-	bool timesup = false;
-	int old_count = break_info.count;
-
-	/* Wait .1 seconds for an oplock break */
-	ne = tevent_timeval_current_ofs(0, 100000);
-
-	if ((te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup))
-	    == NULL)
-	{
-		torture_comment(tctx, "Failed to wait for an oplock break. "
-				      "test results may not be accurate.");
-		goto done;
-	}
-
-	while (!timesup && break_info.count < old_count + 1) {
-		if (tevent_loop_once(tctx->ev) != 0) {
-			torture_comment(tctx, "Failed to wait for an oplock "
-					      "break. test results may not be "
-					      "accurate.");
-			goto done;
-		}
-	}
-
-done:
-	/* We don't know if the timed event fired and was freed, we received
-	 * our oplock break, or some other event triggered the loop.  Thus,
-	 * we create a tmp_ctx to be able to safely free/remove the timed
-	 * event in all 3 cases. */
-	talloc_free(tmp_ctx);
-
-	return;
-}
-
 static bool test_smb2_oplock_exclusive1(struct torture_context *tctx,
 					struct smb2_tree *tree1,
 					struct smb2_tree *tree2)
diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c
index 30863a9167e..0226d46decb 100644
--- a/source4/torture/smb2/oplock_break_handler.c
+++ b/source4/torture/smb2/oplock_break_handler.c
@@ -27,6 +27,7 @@
 #include "torture/smb2/proto.h"
 #include "../libcli/smb/smbXcli_base.h"
 #include "oplock_break_handler.h"
+#include "lib/events/events.h"
 
 struct break_info break_info;
 
@@ -89,3 +90,56 @@ bool torture_oplock_ack_handler(struct smb2_transport *transport,
 
 	return true;
 }
+
+
+/*
+ * Timer handler function notifies the registering function that time is up
+ */
+static void timeout_cb(struct tevent_context *ev,
+		       struct tevent_timer *te,
+		       struct timeval current_time,
+		       void *private_data)
+{
+	bool *timesup = (bool *)private_data;
+	*timesup = true;
+}
+
+/*
+ * Wait a short period of time to receive a single oplock break request
+ */
+void torture_wait_for_oplock_break(struct torture_context *tctx)
+{
+	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+	struct tevent_timer *te = NULL;
+	struct timeval ne;
+	bool timesup = false;
+	int old_count = break_info.count;
+
+	/* Wait .1 seconds for an oplock break */
+	ne = tevent_timeval_current_ofs(0, 100000);
+
+	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
+	if (te == NULL) {
+		torture_comment(tctx, "Failed to wait for an oplock break. "
+				      "test results may not be accurate.");
+		goto done;
+	}
+
+	while (!timesup && break_info.count < old_count + 1) {
+		if (tevent_loop_once(tctx->ev) != 0) {
+			torture_comment(tctx, "Failed to wait for an oplock "
+					      "break. test results may not be "
+					      "accurate.");
+			goto done;
+		}
+	}
+
+done:
+	/* We don't know if the timed event fired and was freed, we received
+	 * our oplock break, or some other event triggered the loop.  Thus,
+	 * we create a tmp_ctx to be able to safely free/remove the timed
+	 * event in all 3 cases.
+	 */
+	talloc_free(tmp_ctx);
+}
+
diff --git a/source4/torture/smb2/oplock_break_handler.h b/source4/torture/smb2/oplock_break_handler.h
index f3216d6d095..88630178dee 100644
--- a/source4/torture/smb2/oplock_break_handler.h
+++ b/source4/torture/smb2/oplock_break_handler.h
@@ -37,6 +37,7 @@ bool torture_oplock_ack_handler(struct smb2_transport *transport,
 				const struct smb2_handle *handle,
 				uint8_t level,
 				void *private_data);
+void torture_wait_for_oplock_break(struct torture_context *tctx);
 
 static inline void torture_reset_break_info(struct torture_context *tctx,
 			      struct break_info *r)
diff --git a/source4/torture/smb2/replay.c b/source4/torture/smb2/replay.c
index 1085f2dd405..51c6e4a7d71 100644
--- a/source4/torture/smb2/replay.c
+++ b/source4/torture/smb2/replay.c
@@ -95,61 +95,6 @@
 
 #define BASEDIR "replaytestdir"
 
-/**
- * Timer handler function notifies the registering function that time is up
- */
-static void timeout_cb(struct tevent_context *ev,
-		       struct tevent_timer *te,
-		       struct timeval current_time,
-		       void *private_data)
-{
-	bool *timesup = (bool *)private_data;
-	*timesup = true;
-	return;
-}
-
-/**
- *  Wait a short period of time to receive a single oplock break request
- */
-static void torture_wait_for_oplock_break(struct torture_context *tctx)
-{
-	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
-	struct tevent_timer *te = NULL;
-	struct timeval ne;
-	bool timesup = false;
-	int old_count = break_info.count;
-
-	/* Wait .1 seconds for an oplock break */
-	ne = tevent_timeval_current_ofs(0, 100000);
-
-	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
-	if (te == NULL) {
-		torture_comment(tctx, "Failed to wait for an oplock break. "
-				      "test results may not be accurate.");
-		goto done;
-	}
-
-	while (!timesup && break_info.count < old_count + 1) {
-		if (tevent_loop_once(tctx->ev) != 0) {
-			torture_comment(tctx, "Failed to wait for an oplock "
-					      "break. test results may not be "
-					      "accurate.");
-			goto done;
-		}
-	}
-
-done:
-	/*
-	 * We don't know if the timed event fired and was freed, we received
-	 * our oplock break, or some other event triggered the loop.  Thus,
-	 * we create a tmp_ctx to be able to safely free/remove the timed
-	 * event in all 3 cases.
-	 */
-	talloc_free(tmp_ctx);
-
-	return;
-}
-
 /**
  * Test what happens when SMB2_FLAGS_REPLAY_OPERATION is enabled for various
  * commands. We want to verify if the server returns an error code or not.
-- 
2.20.1


From 7930c6d91d9e1b3ddc2f6370231db46eac7669e8 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Sat, 16 Mar 2019 12:11:04 +0000
Subject: [PATCH 05/19] s4-torture: Add function declarations to
 lease_break_handler.h

Do not completely depend on proto.h.

Also move torture_reset_break_info() to lease_break_handler.h so that
the layout is similar to that of oplock_break_handler.*

Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/lease_break_handler.c |  8 --------
 source4/torture/smb2/lease_break_handler.h | 13 +++++++++++--
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/source4/torture/smb2/lease_break_handler.c b/source4/torture/smb2/lease_break_handler.c
index b70234420bd..5f8e325d63f 100644
--- a/source4/torture/smb2/lease_break_handler.c
+++ b/source4/torture/smb2/lease_break_handler.c
@@ -124,11 +124,3 @@ done:
 
 	return;
 }
-
- void torture_reset_lease_break_info(struct torture_context *tctx,
-				     struct lease_break_info *r)
-{
-	ZERO_STRUCTP(r);
-	r->tctx = tctx;
-}
-
diff --git a/source4/torture/smb2/lease_break_handler.h b/source4/torture/smb2/lease_break_handler.h
index dc8841bb8a1..1e915e27951 100644
--- a/source4/torture/smb2/lease_break_handler.h
+++ b/source4/torture/smb2/lease_break_handler.h
@@ -118,5 +118,14 @@ struct lease_break_info {
 
 extern struct lease_break_info lease_break_info;
 
-void torture_reset_lease_break_info(struct torture_context *tctx,
-				    struct lease_break_info *r);
+bool torture_lease_handler(struct smb2_transport *transport,
+			   const struct smb2_lease_break *lb,
+			   void *private_data);
+void torture_wait_for_lease_break(struct torture_context *tctx);
+
+static inline void torture_reset_lease_break_info(struct torture_context *tctx,
+						  struct lease_break_info *r)
+{
+	ZERO_STRUCTP(r);
+	r->tctx = tctx;
+}
-- 
2.20.1


From 87ab867fe5a5bcad745a923cfa0537dce93aac0f Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Sat, 16 Mar 2019 12:25:07 +0000
Subject: [PATCH 06/19] s4-torture: Add handlers to ignore incoming
 oplock/lease break requests

For use in multichannel oplock break tests. These handers ignore
incoming oplock and lease break requests so that we can test the
oplock/lease break retries on the server.

This is meant for use with samba servers which rely on receiving a reply
from the client before timeout.
Windows servers rely on underlying tcp commands to decide if the oplock
break command was delivered successfully to the client and therefore
cannot be tested with this method.

Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/lease_break_handler.c  | 12 ++++++++++++
 source4/torture/smb2/lease_break_handler.h  |  3 +++
 source4/torture/smb2/oplock_break_handler.c | 10 ++++++++++
 source4/torture/smb2/oplock_break_handler.h |  4 ++++
 4 files changed, 29 insertions(+)

diff --git a/source4/torture/smb2/lease_break_handler.c b/source4/torture/smb2/lease_break_handler.c
index 5f8e325d63f..8e576391eb1 100644
--- a/source4/torture/smb2/lease_break_handler.c
+++ b/source4/torture/smb2/lease_break_handler.c
@@ -72,6 +72,18 @@ bool torture_lease_handler(struct smb2_transport *transport,
 	return true;
 }
 
+/*
+ * A lease break handler which ignores incoming lease break requests
+ * To be used in cases where the client is expected to ignore incoming
+ * lease break requests
+ */
+bool torture_lease_ignore_handler(struct smb2_transport *transport,
+			   const struct smb2_lease_break *lb,
+			   void *private_data)
+{
+	return true;
+}
+
 /*
    Timer handler function notifies the registering function that time is up
 */
diff --git a/source4/torture/smb2/lease_break_handler.h b/source4/torture/smb2/lease_break_handler.h
index 1e915e27951..90fde1a9217 100644
--- a/source4/torture/smb2/lease_break_handler.h
+++ b/source4/torture/smb2/lease_break_handler.h
@@ -121,6 +121,9 @@ extern struct lease_break_info lease_break_info;
 bool torture_lease_handler(struct smb2_transport *transport,
 			   const struct smb2_lease_break *lb,
 			   void *private_data);
+bool torture_lease_ignore_handler(struct smb2_transport *transport,
+				  const struct smb2_lease_break *lb,
+				  void *private_data);
 void torture_wait_for_lease_break(struct torture_context *tctx);
 
 static inline void torture_reset_lease_break_info(struct torture_context *tctx,
diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c
index 0226d46decb..1c2cac83fc0 100644
--- a/source4/torture/smb2/oplock_break_handler.c
+++ b/source4/torture/smb2/oplock_break_handler.c
@@ -91,6 +91,16 @@ bool torture_oplock_ack_handler(struct smb2_transport *transport,
 	return true;
 }
 
+/**
+ * A oplock break handler designed to ignore incoming break requests.
+ * This is used when incoming oplock break requests need to be ignored
+ */
+bool torture_oplock_ignore_handler(struct smb2_transport *transport,
+				   const struct smb2_handle *handle,
+				   uint8_t level, void *private_data)
+{
+	return true;
+}
 
 /*
  * Timer handler function notifies the registering function that time is up
diff --git a/source4/torture/smb2/oplock_break_handler.h b/source4/torture/smb2/oplock_break_handler.h
index 88630178dee..a7493697d55 100644
--- a/source4/torture/smb2/oplock_break_handler.h
+++ b/source4/torture/smb2/oplock_break_handler.h
@@ -37,6 +37,10 @@ bool torture_oplock_ack_handler(struct smb2_transport *transport,
 				const struct smb2_handle *handle,
 				uint8_t level,
 				void *private_data);
+bool torture_oplock_ignore_handler(struct smb2_transport *transport,
+				const struct smb2_handle *handle,
+				uint8_t level,
+				void *private_data);
 void torture_wait_for_oplock_break(struct torture_context *tctx);
 
 static inline void torture_reset_break_info(struct torture_context *tctx,
-- 
2.20.1


From d577b7d152f390bc8c441011ff962e43029ad0a1 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 28 May 2018 17:24:54 +0530
Subject: [PATCH 07/19] s4-torture: Increase timeout for lease/oplock break
 handlers

0.1 seconds is not enough when running tests against a server over the
network and are causing timing related bugs. We increase this to 1
second.

Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/lease_break_handler.c  | 4 ++--
 source4/torture/smb2/oplock_break_handler.c | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/source4/torture/smb2/lease_break_handler.c b/source4/torture/smb2/lease_break_handler.c
index 8e576391eb1..d741127f3d5 100644
--- a/source4/torture/smb2/lease_break_handler.c
+++ b/source4/torture/smb2/lease_break_handler.c
@@ -108,8 +108,8 @@ void torture_wait_for_lease_break(struct torture_context *tctx)
 	bool timesup = false;
 	int old_count = lease_break_info.count;
 
-	/* Wait .1 seconds for an lease break */
-	ne = tevent_timeval_current_ofs(0, 100000);
+	/* Wait 1 second for an lease break */
+	ne = tevent_timeval_current_ofs(0, 1000000);
 
 	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
 	if (te == NULL) {
diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c
index 1c2cac83fc0..7bf8bb58963 100644
--- a/source4/torture/smb2/oplock_break_handler.c
+++ b/source4/torture/smb2/oplock_break_handler.c
@@ -125,8 +125,8 @@ void torture_wait_for_oplock_break(struct torture_context *tctx)
 	bool timesup = false;
 	int old_count = break_info.count;
 
-	/* Wait .1 seconds for an oplock break */
-	ne = tevent_timeval_current_ofs(0, 100000);
+	/* Wait 1 second for an oplock break */
+	ne = tevent_timeval_current_ofs(0, 1000000);
 
 	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
 	if (te == NULL) {
-- 
2.20.1


From 0486ef15971720362610dd2131bce46bc1bca68d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Tue, 19 Jan 2016 15:39:34 +0100
Subject: [PATCH 08/19] s4-torture: add test for interface information
 retrieval for multichannel.

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/multichannel.c | 58 +++++++++++++++++++++++++++--
 1 file changed, 54 insertions(+), 4 deletions(-)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 2c72bd779d5..b15a7ed1998 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -27,6 +27,8 @@
 #include "torture/smb2/proto.h"
 #include "libcli/security/security.h"
 #include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_ioctl.h"
+#include "../libcli/smb/smbXcli_base.h"
 
 #define CHECK_STATUS(status, correct) do { \
 	if (!NT_STATUS_EQUAL(status, correct)) { \
@@ -36,19 +38,67 @@
 		return false; \
 	} } while (0)
 
-static bool test_session_bind(struct torture_context *tctx,
-			      struct smb2_tree *tree)
+static bool test_ioctl_network_interface_info(struct torture_context *tctx,
+					      struct smb2_tree *tree,
+					      struct fsctl_net_iface_info *info)
 {
-	/* TODO */
+	union smb_ioctl ioctl;
+	struct smb2_handle fh;
+	uint32_t caps;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
+		torture_skip(tctx,
+			    "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
+	}
+
+	ZERO_STRUCT(ioctl);
+
+	ioctl.smb2.level = RAW_IOCTL_SMB2;
+
+	fh.data[0] = UINT64_MAX;
+	fh.data[1] = UINT64_MAX;
+
+	ioctl.smb2.in.file.handle = fh;
+	ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
+	/* Windows client sets this to 64KiB */
+	ioctl.smb2.in.max_response_size = 0x10000;
+	ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
+
+	torture_assert_ntstatus_ok(tctx,
+		smb2_ioctl(tree, tctx, &ioctl.smb2),
+		"FSCTL_QUERY_NETWORK_INTERFACE_INFO failed");
+
+	torture_assert(tctx,
+		(ioctl.smb2.out.out.length != 0),
+		"no interface info returned???");
+
+	torture_assert_ndr_success(tctx,
+		ndr_pull_struct_blob(&ioctl.smb2.out.out, tctx, info,
+			(ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info),
+		"failed to ndr pull");
+
+	if (DEBUGLVL(1)) {
+		NDR_PRINT_DEBUG(fsctl_net_iface_info, info);
+	}
 
 	return true;
 }
 
+static bool test_multichannel_interface_info(struct torture_context *tctx,
+					     struct smb2_tree *tree)
+{
+	struct fsctl_net_iface_info info;
+
+	return test_ioctl_network_interface_info(tctx, tree, &info);
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
 
-	torture_suite_add_1smb2_test(suite, "session-bind", test_session_bind);
+	torture_suite_add_1smb2_test(suite, "interface_info",
+				     test_multichannel_interface_info);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From d4e808815c99d1e589ac050152dcd38167e704af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Thu, 29 Sep 2016 06:49:50 +0200
Subject: [PATCH 09/19] s4-torture: add torture_block/torture_unblock smb2
 transport functions

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/block.c       | 372 +++++++++++++++++++++++++++++
 source4/torture/smb2/block.h       |  45 ++++
 source4/torture/smb2/wscript_build |   1 +
 3 files changed, 418 insertions(+)
 create mode 100644 source4/torture/smb2/block.c
 create mode 100644 source4/torture/smb2/block.h

diff --git a/source4/torture/smb2/block.c b/source4/torture/smb2/block.c
new file mode 100644
index 00000000000..075816b0334
--- /dev/null
+++ b/source4/torture/smb2/block.c
@@ -0,0 +1,372 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * block SMB2 transports using iptables
+ *
+ * Copyright (C) Guenther Deschner, 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 "torture/torture.h"
+#include "torture/smb2/proto.h"
+#include "system/network.h"
+#include "lib/util/util_net.h"
+#include "torture/smb2/block.h"
+#include "libcli/smb/smbXcli_base.h"
+
+/*
+ * INPUT
+ *  |
+ *  -----> SAMBA_INPUT
+ *             |
+ *             -----> SAMBA_INPUT_transportname1
+ *             -----> SAMBA_INPUT_transportname2
+ */
+
+
+static bool run_cmd(const char *cmd)
+{
+	int ret;
+
+	DEBUG(10, ("%s will call '%s'\n", __location__, cmd));
+
+	ret = system(cmd);
+	if (ret) {
+		DEBUG(1, ("%s failed to execute system call: %s: %d\n",
+			__location__, cmd, ret));
+		return false;
+	}
+
+	return true;
+}
+
+int smbrun(const char *cmd, int *outfd, char * const *env);
+
+static bool run_cmd_return_buf(TALLOC_CTX *mem_ctx,
+			       const char *cmd,
+			       int *num_lines, char ***buf)
+{
+	int ret;
+	int fd = -1;
+
+	DEBUG(10, ("%s will call '%s'\n", __location__, cmd));
+
+	ret = smbrun(cmd, &fd, NULL);
+	if (ret) {
+		DEBUG(1, ("%s failed to execute system call: %s: %d\n",
+			__location__, cmd, ret));
+		if (fd != -1) {
+			close(fd);
+		}
+		return false;
+	}
+
+	*buf = fd_lines_load(fd, num_lines, 0, mem_ctx);
+	if (fd != -1) {
+		close(fd);
+	}
+	if (*buf == NULL) {
+		return false;
+	}
+
+	return true;
+}
+
+static const char *iptables_command(struct torture_context *tctx)
+{
+	return torture_setting_string(tctx, "iptables_command",
+				      "/usr/sbin/iptables");
+}
+
+char *escape_shell_string(const char *src);
+
+/*
+ * iptables v1.6.1: chain name `SAMBA_INPUT_tree1->session->transport'
+ * too long (must be under 29 chars)
+ *
+ * maybe truncate chainname ?
+ */
+static const char *samba_chain_name(struct torture_context *tctx,
+				    const char *name,
+				    bool input)
+{
+	const char *s;
+	char *sm;
+
+	s = talloc_asprintf(tctx, "%s_%s",
+			    input ? "SAMBA_INPUT" : "SAMBA_OUTPUT",
+			    name);
+	if (s == NULL) {
+		return NULL;
+	}
+
+	sm = escape_shell_string(s);
+	if (sm == NULL) {
+		return NULL;
+	}
+
+	s = talloc_strdup(tctx, sm);
+	free(sm);
+
+	return s;
+}
+
+static bool filter_tcp_setup(struct torture_context *tctx,
+			     bool unblock)
+{
+	const char *cmd_in, *cmd_out;
+	const char *ipt = iptables_command(tctx);
+
+	if (unblock) {
+		cmd_in = talloc_asprintf(tctx,
+				"%s -L SAMBA_INPUT > /dev/null 2>&1 && "
+				"("
+				"%s -F SAMBA_INPUT; "
+				"%s -D INPUT -j SAMBA_INPUT; "
+				"%s -X SAMBA_INPUT;"
+				")",
+				ipt, ipt, ipt, ipt);
+		cmd_out = talloc_asprintf(tctx,
+				"%s -L SAMBA_OUTPUT > /dev/null 2>&1 && "
+				"("
+				"%s -F SAMBA_OUTPUT;"
+				"%s -D OUTPUT -j SAMBA_OUTPUT;"
+				"%s -X SAMBA_OUTPUT;"
+				")",
+				ipt, ipt, ipt, ipt);
+	} else {
+		cmd_in = talloc_asprintf(tctx,
+				"%s -L SAMBA_INPUT > /dev/null 2>&1 || "
+				"("
+				"%s -N SAMBA_INPUT && "
+				"%s -I INPUT -j SAMBA_INPUT "
+				")",
+				ipt, ipt, ipt);
+		cmd_out = talloc_asprintf(tctx,
+				"%s -L SAMBA_OUTPUT > /dev/null 2>&1 || "
+				"("
+				"%s -N SAMBA_OUTPUT && "
+				"%s -I OUTPUT -j SAMBA_OUTPUT;"
+				")",
+				ipt, ipt, ipt);
+	}
+
+	if (cmd_in == NULL || cmd_out == NULL) {
+		return false;
+	}
+
+	if (!run_cmd(cmd_in)) {
+		return false;
+	}
+	/* if (!run_cmd(cmd_out)) { return false; } */
+
+	return true;
+}
+
+static bool filter_tcp_setup_name(struct torture_context *tctx,
+				  const char *name, bool unblock)
+{
+	const char *cmd_in, *cmd_out;
+	const char *chain_in, *chain_out;
+	const char *ipt = iptables_command(tctx);
+
+	chain_in = samba_chain_name(tctx, name, true);
+	chain_out = samba_chain_name(tctx, name, false);
+	if (chain_in == NULL || chain_out == NULL) {
+		return false;
+	}
+
+	if (unblock) {
+		cmd_in  = talloc_asprintf(tctx, "%s -F %s; "
+						"%s -D SAMBA_INPUT -j %s; "
+						"%s -X %s",
+						ipt, chain_in,
+						ipt, chain_in,
+						ipt, chain_in);
+		cmd_out = talloc_asprintf(tctx, "%s -F %s; "
+						"%s -D SAMBA_OUTPUT -j %s; "
+						"%s -X %s",
+						ipt, chain_out,
+						ipt, chain_out,
+						ipt, chain_out);
+	} else {
+		cmd_in  = talloc_asprintf(tctx, "%s -L %s > /dev/null 2>&1 || "
+						"%s -N %s && "
+						"%s -I SAMBA_INPUT -j %s",
+						ipt, chain_in,
+						ipt, chain_in,
+						ipt, chain_in);
+		cmd_out = talloc_asprintf(tctx, "%s -L %s > /dev/null 2>&1 || "
+						"%s -N %s && "
+						"%s -I SAMBA_OUTPUT -j %s",
+						ipt, chain_out,
+						ipt, chain_out,
+						ipt, chain_out);
+	}
+
+	if (cmd_in == NULL || cmd_out == NULL) {
+		return false;
+	}
+
+	if (!run_cmd(cmd_in)) {
+		return false;
+	}
+	/* if (!run_cmd(cmd_out)) return false; */
+
+	return true;
+}
+
+/* '11   452 DROP tcp -- * *  0.0.0.0/0  0.0.0.0/0  tcp dpt:43062' */
+static bool get_packet_count(const char *s, uint32_t *count)
+{
+	int i = 0;
+	char *p;
+
+	if (s == NULL) {
+		return false;
+	}
+
+	while (s[i] == ' ') {
+		s++;
+	}
+
+	p = strchr(s, ' ');
+	if (p == NULL) {
+		return false;
+	}
+	*p = '\0';
+
+	*count = atoi(s);
+
+	return true;
+}
+
+bool torture_list_tcp_transport_name(struct torture_context *tctx,
+				    const char *name,
+				    uint32_t *_packets)
+{
+	const char *chain_in, *cmd;
+	int num_lines;
+	char **buf;
+	uint32_t packets = 0;
+	const char *ipt = iptables_command(tctx);
+
+	chain_in = samba_chain_name(tctx, name, true);
+	if (chain_in == NULL) {
+		return false;
+	}
+
+	cmd = talloc_asprintf(tctx, "%s -L %s -v -n", ipt, chain_in);
+	if (cmd == NULL) {
+		return false;
+	}
+
+	if (!run_cmd_return_buf(tctx, cmd, &num_lines, &buf)) {
+		return false;
+	}
+	SMB_ASSERT(num_lines >= 3);
+
+	if (!get_packet_count(buf[2], &packets)) {
+		return false;
+	}
+
+	torture_comment(tctx, "chain: '%s', packets: %d\n", name, (int)packets);
+
+	if (_packets != NULL) {
+		*_packets = packets;
+	}
+
+	return true;
+}
+
+uint16_t torture_get_local_port_from_transport(struct smb2_transport *t)
+{
+	const struct sockaddr_storage *local_ss;
+
+	local_ss = smbXcli_conn_local_sockaddr(t->conn);
+
+	return get_sockaddr_port(local_ss);
+}
+
+static bool torture_block_tcp_transport_name_internal(
+						struct torture_context *tctx,
+						struct smb2_transport *t,
+						const char *name,
+						bool unblock)
+{
+	char *cmd_in;
+	char *cmd_out;
+	const char *chain_in, *chain_out;
+	uint16_t port = torture_get_local_port_from_transport(t);
+	const char *ipt = iptables_command(tctx);
+
+	chain_in = samba_chain_name(tctx, name, true);
+	chain_out = samba_chain_name(tctx, name, false);
+	if (chain_in == NULL || chain_out == NULL) {
+		return false;
+	}
+
+	if (!unblock) {
+		filter_tcp_setup(tctx, false);
+		filter_tcp_setup_name(tctx, name, false);
+	}
+
+	torture_comment(tctx, "%sblocking %s dport %d\n",
+			unblock ? "un" : "", name, port);
+
+	cmd_in = talloc_asprintf(tctx,
+				 "%s %s %s -p tcp --dport %d -j DROP",
+				 ipt, unblock ? "-D" : "-I", chain_in, port);
+	cmd_out = talloc_asprintf(tctx,
+				  "%s %s %s -p tcp --sport %d -j DROP",
+				  ipt, unblock ? "-D" : "-I", chain_out, port);
+	if (cmd_in == NULL || cmd_out == NULL) {
+		return false;
+	}
+
+	if (!run_cmd(cmd_in)) {
+		return false;
+	}
+	/* if (!run_cmd(cmd_out)) return false; */
+
+	if (unblock) {
+		filter_tcp_setup_name(tctx, name, true);
+		/* better dont cleanup here */
+		/* filter_tcp_setup(tctx, true); */
+	}
+
+	return true;
+}
+
+bool torture_block_tcp_transport_name(struct torture_context *tctx,
+				      struct smb2_transport *t,
+				      const char *name)
+{
+	return torture_block_tcp_transport_name_internal(tctx, t, name, false);
+}
+
+bool torture_unblock_tcp_transport_name(struct torture_context *tctx,
+					struct smb2_transport *t,
+					const char *name)
+{
+	return torture_block_tcp_transport_name_internal(tctx, t, name, true);
+}
+
+void torture_unblock_cleanup(struct torture_context *tctx)
+{
+	filter_tcp_setup(tctx, true);
+}
diff --git a/source4/torture/smb2/block.h b/source4/torture/smb2/block.h
new file mode 100644
index 00000000000..9278caaa2b7
--- /dev/null
+++ b/source4/torture/smb2/block.h
@@ -0,0 +1,45 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * block SMB2 transports using iptables
+ *
+ * Copyright (C) Guenther Deschner, 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/>.
+ */
+
+bool torture_list_tcp_transport_name(struct torture_context *tctx,
+				    const char *name,
+				    uint32_t *packets);
+
+bool torture_block_tcp_transport_name(struct torture_context *tctx,
+				      struct smb2_transport *t,
+				      const char *name);
+
+bool torture_unblock_tcp_transport_name(struct torture_context *tctx,
+					struct smb2_transport *t,
+					const char *name);
+
+void torture_unblock_cleanup(struct torture_context *tctx);
+
+uint16_t torture_get_local_port_from_transport(struct smb2_transport *t);
+
+#define torture_block_tcp_transport(_tctx, _t) \
+	torture_block_tcp_transport_name(_tctx, _t, #_t)
+
+#define torture_unblock_tcp_transport(_tctx, _t) \
+	torture_unblock_tcp_transport_name(_tctx, _t, #_t)
+
+#define torture_list_tcp_transport(_tctx, _t, _packets) \
+	torture_list_tcp_transport_name(_tctx, #_t, _packets)
diff --git a/source4/torture/smb2/wscript_build b/source4/torture/smb2/wscript_build
index 1183cb93772..e605a4589ac 100644
--- a/source4/torture/smb2/wscript_build
+++ b/source4/torture/smb2/wscript_build
@@ -3,6 +3,7 @@
 bld.SAMBA_MODULE('TORTURE_SMB2',
 	source='''
         acls.c
+        block.c
         compound.c
         connect.c
         create.c
-- 
2.20.1


From 4a73f69d6184c255063bf15e93886949bf0db771 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 17:14:44 +0000
Subject: [PATCH 10/19] s4-torture: Add #defines required by the new tests

New macros used by our tests.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 73 +++++++++++++++++++++++++++--
 1 file changed, 68 insertions(+), 5 deletions(-)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index b15a7ed1998..3692ffb695b 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -30,14 +30,77 @@
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "../libcli/smb/smbXcli_base.h"
 
-#define CHECK_STATUS(status, correct) do { \
-	if (!NT_STATUS_EQUAL(status, correct)) { \
+#define BASEDIR "multichanneltestdir"
+
+#define CHECK_STATUS(status, correct) \
+	torture_assert_ntstatus_equal_goto(tctx, status, correct,\
+					   ret, done, "")
+
+#define CHECK_VAL(v, correct) do { \
+	if ((v) != (correct)) { \
+		torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s" \
+				" got 0x%x - should be 0x%x\n", \
+				__location__, #v, (int)v, (int)correct); \
+		ret = false; \
+		goto done; \
+	} } while (0)
+
+#define CHECK_VAL_GREATER_THAN(v, gt_val) do { \
+	if ((v) <= (gt_val)) { \
 		torture_result(tctx, TORTURE_FAIL, \
-			"(%s) Incorrect status %s - should be %s\n", \
-			 __location__, nt_errstr(status), nt_errstr(correct)); \
-		return false; \
+				"(%s): wrong value for %s got 0x%x - " \
+				"should be greater than 0x%x\n", \
+				__location__, #v, (int)v, (int)gt_val); \
+		ret = false; \
+		goto done; \
 	} } while (0)
 
+#define CHECK_CREATED(__io, __created, __attribute)			\
+	do {								\
+		CHECK_VAL((__io)->out.create_action,			\
+				NTCREATEX_ACTION_ ## __created);	\
+		CHECK_VAL((__io)->out.alloc_size, 0);			\
+		CHECK_VAL((__io)->out.size, 0);				\
+		CHECK_VAL((__io)->out.file_attr, (__attribute));	\
+		CHECK_VAL((__io)->out.reserved2, 0);			\
+	} while (0)
+
+#define CHECK_PTR(ptr, correct) do { \
+	if ((ptr) != (correct)) { \
+		torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \
+				"got 0x%p - should be 0x%p\n", \
+				__location__, #ptr, ptr, correct); \
+		ret = false; \
+		goto done; \
+	} } while (0)
+
+#define CHECK_LEASE(__io, __state, __oplevel, __key, __flags)		\
+	do {								\
+		CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
+		if (__oplevel) {					\
+			CHECK_VAL((__io)->out.oplock_level, \
+					SMB2_OPLOCK_LEVEL_LEASE); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
+				  (__key)); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
+				  ~(__key)); \
+			CHECK_VAL((__io)->out.lease_response.lease_state,\
+				  smb2_util_lease_state(__state)); \
+		} else {						\
+			CHECK_VAL((__io)->out.oplock_level,\
+				  SMB2_OPLOCK_LEVEL_NONE); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
+				  0); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
+				  0); \
+			CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
+		}							\
+									\
+		CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \
+		CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
+		CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
+	} while (0)
+
 static bool test_ioctl_network_interface_info(struct torture_context *tctx,
 					      struct smb2_tree *tree,
 					      struct fsctl_net_iface_info *info)
-- 
2.20.1


From c9e05e9491e3f2d78a6c277c156539f71bcc408f Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Thu, 28 Feb 2019 12:09:08 +0000
Subject: [PATCH 11/19] s4-torture: Add helper functions to create channels.

Helper functions used by both oplock and lease break tests.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 145 +++++++++++++++++++++++++++-
 1 file changed, 144 insertions(+), 1 deletion(-)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 3692ffb695b..ab022249eb7 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -23,12 +23,13 @@
 #include "libcli/smb2/smb2.h"
 #include "libcli/smb2/smb2_calls.h"
 #include "torture/torture.h"
-#include "torture/util.h"
 #include "torture/smb2/proto.h"
 #include "libcli/security/security.h"
 #include "librpc/gen_ndr/ndr_security.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "../libcli/smb/smbXcli_base.h"
+#include "libcli/resolve/resolve.h"
+#include "lib/param/param.h"
 
 #define BASEDIR "multichanneltestdir"
 
@@ -156,6 +157,148 @@ static bool test_multichannel_interface_info(struct torture_context *tctx,
 	return test_ioctl_network_interface_info(tctx, tree, &info);
 }
 
+static struct smb2_tree *test_multichannel_create_channel(
+				struct torture_context *tctx,
+				const char *host,
+				const char *share,
+				struct cli_credentials *credentials,
+				struct smbcli_options *transport_options,
+				struct smb2_tree *parent_tree
+				)
+{
+	NTSTATUS status;
+	struct smb2_transport *transport;
+	struct smb2_session *session;
+	bool ret = true;
+	struct smb2_tree *tree;
+
+	status = smb2_connect(tctx,
+			host,
+			lpcfg_smb_ports(tctx->lp_ctx),
+			share,
+			lpcfg_resolve_context(tctx->lp_ctx),
+			credentials,
+			&tree,
+			tctx->ev,
+			transport_options,
+			lpcfg_socket_options(tctx->lp_ctx),
+			lpcfg_gensec_settings(tctx, tctx->lp_ctx)
+			);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+			"smb2_connect failed");
+	transport = tree->session->transport;
+	transport->oplock.handler = torture_oplock_ack_handler;
+	transport->oplock.private_data = tree;
+	transport->lease.handler = torture_lease_handler;
+	transport->lease.private_data = tree;
+	torture_comment(tctx, "established transport [%p]\n", transport);
+
+	/*
+	 * If parent tree is set, bind the session to the parent transport
+	 */
+	if (parent_tree) {
+		session = smb2_session_channel(transport,
+				lpcfg_gensec_settings(tctx, tctx->lp_ctx),
+				parent_tree, parent_tree->session);
+		torture_assert_goto(tctx, session != NULL, ret, done,
+				"smb2_session_channel failed");
+
+		tree->smbXcli = parent_tree->smbXcli;
+		tree->session = session;
+		status = smb2_session_setup_spnego(session,
+						credentials,
+						0 /* previous_session_id */);
+		CHECK_STATUS(status, NT_STATUS_OK);
+		torture_comment(tctx, "bound new session to parent\n");
+	}
+	/*
+	 * We absolutely need to make sure to send something over this
+	 * connection to register the oplock break handler with the smb client
+	 * connection. If we do not send something (at least a keepalive), we
+	 * will *NEVER* receive anything over this transport.
+	 */
+	smb2_keepalive(transport);
+
+	return tree;
+done:
+	return NULL;
+}
+
+bool test_multichannel_create_channels(
+				struct torture_context *tctx,
+				const char *host,
+				const char *share,
+				struct cli_credentials *credentials,
+				struct smbcli_options *transport_options,
+				struct smb2_tree **tree2A,
+				struct smb2_tree **tree2B,
+				struct smb2_tree **tree2C
+				)
+{
+	struct smb2_tree *tree;
+	struct smb2_transport *transport2A;
+	struct smb2_transport *transport2B;
+	struct smb2_transport *transport2C;
+	uint16_t local_port = 0;
+
+	transport_options->client_guid = GUID_random();
+
+	/* Session 2A */
+	torture_comment(tctx, "Setting up connection 2A\n");
+	tree = test_multichannel_create_channel(tctx, host, share,
+				credentials, transport_options, NULL);
+	if (!tree) {
+		goto done;
+	}
+	*tree2A = tree;
+	transport2A = tree->session->transport;
+	local_port = torture_get_local_port_from_transport(transport2A);
+	torture_comment(tctx, "transport2A uses tcp port: %d\n", local_port);
+
+	/* Session 2B */
+	if (tree2B) {
+		torture_comment(tctx, "Setting up connection 2B\n");
+		tree = test_multichannel_create_channel(tctx, host, share,
+				credentials, transport_options, *tree2A);
+		if (!tree) {
+			goto done;
+		}
+		*tree2B = tree;
+		transport2B = tree->session->transport;
+		local_port = torture_get_local_port_from_transport(transport2B);
+		torture_comment(tctx, "transport2B uses tcp port: %d\n",
+								local_port);
+	}
+
+	/* Session 2C */
+	if (tree2C) {
+		torture_comment(tctx, "Setting up connection 2C\n");
+		tree = test_multichannel_create_channel(tctx, host, share,
+				credentials, transport_options, *tree2A);
+		if (!tree) {
+			goto done;
+		}
+		*tree2C = tree;
+		transport2C = tree->session->transport;
+		local_port = torture_get_local_port_from_transport(transport2C);
+		torture_comment(tctx, "transport2C uses tcp port: %d\n",
+								local_port);
+	}
+
+	return true;
+done:
+	return false;
+}
+
+static void test_multichannel_free_channels(struct smb2_tree *tree2A,
+					     struct smb2_tree *tree2B,
+					     struct smb2_tree *tree2C)
+{
+	TALLOC_FREE(tree2A);
+	TALLOC_FREE(tree2B);
+	TALLOC_FREE(tree2C);
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
-- 
2.20.1


From 7c8d2261d16e94f40dc9dd662915c69204b4a2a6 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Thu, 28 Feb 2019 12:51:02 +0000
Subject: [PATCH 12/19] s4-torture: Add handlers to block channels for testing

We use two methods to block channels

1) Simply ignore incoming oplock break requests and do not respond to
them.
This method doesn't work against Microsoft Windows based servers which
rely on the tcp stack for confirmation that the oplock break command was
sent to the client machine. This is meant to be used with samba servers
and is the default method.

2) Use iptables to block the channel.
The method requires the use of a privileged account and can only be used
on Linux systems with iptables installed. To use this blocking method,
pass the option
--option=torture:use_iptables=true

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 97 +++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index ab022249eb7..831a94878e5 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -30,6 +30,8 @@
 #include "../libcli/smb/smbXcli_base.h"
 #include "libcli/resolve/resolve.h"
 #include "lib/param/param.h"
+#include "oplock_break_handler.h"
+#include "torture/smb2/block.h"
 
 #define BASEDIR "multichanneltestdir"
 
@@ -299,6 +301,101 @@ static void test_multichannel_free_channels(struct smb2_tree *tree2A,
 	TALLOC_FREE(tree2C);
 }
 
+/*
+ * We simulate blocking incoming oplock break requests by simply ignoring
+ * the incoming break requests.
+ */
+static bool test_set_ignore_break_handler(struct torture_context *tctx,
+					  struct smb2_transport *transport)
+{
+	transport->oplock.handler = torture_oplock_ignore_handler;
+	transport->lease.handler = torture_lease_ignore_handler;
+
+	return true;
+}
+
+static bool test_reset_break_handler(struct torture_context *tctx,
+				     struct smb2_transport *transport)
+{
+	transport->oplock.handler = torture_oplock_ack_handler;
+	transport->lease.handler = torture_lease_handler;
+
+	return true;
+}
+
+/*
+ * Use iptables to block channels
+ */
+static bool test_iptables_block_channel(struct torture_context *tctx,
+					struct smb2_transport *transport,
+					char *name)
+{
+	uint16_t local_port;
+	bool ret;
+
+	local_port = torture_get_local_port_from_transport(transport);
+	torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
+	ret = torture_block_tcp_transport_name(tctx, transport, name);
+	torture_assert(tctx, ret, "we could not block tcp transport");
+
+	return ret;
+}
+
+static bool test_iptables_unblock_channel(struct torture_context *tctx,
+					  struct smb2_transport *transport,
+					  char *name)
+{
+	uint16_t local_port;
+	bool ret;
+
+	local_port = torture_get_local_port_from_transport(transport);
+	torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
+	ret = torture_unblock_tcp_transport_name(tctx, transport, name);
+	torture_assert(tctx, ret, "we could not block tcp transport");
+
+	return ret;
+}
+
+#define test_block_channel(_tctx, _t) _test_block_channel(_tctx, _t, #_t)
+static bool _test_block_channel(struct torture_context *tctx,
+					  struct smb2_transport *transport,
+					  char *name)
+{
+	bool use_iptables = torture_setting_bool(tctx,
+					"use_iptables", false);
+
+	if (use_iptables) {
+		return test_iptables_block_channel(tctx, transport, name);
+	} else {
+		return test_set_ignore_break_handler(tctx, transport);
+	}
+}
+
+#define test_unblock_channel(_tctx, _t) _test_unblock_channel(_tctx, _t, #_t)
+static bool _test_unblock_channel(struct torture_context *tctx,
+					  struct smb2_transport *transport,
+					  char *name)
+{
+	bool use_iptables = torture_setting_bool(tctx,
+					"use_iptables", false);
+
+	if (use_iptables) {
+		return test_iptables_unblock_channel(tctx, transport, name);
+	} else {
+		return test_reset_break_handler(tctx, transport);
+	}
+}
+
+static void test_cleanup_blocked_channels(struct torture_context *tctx)
+{
+	bool use_iptables = torture_setting_bool(tctx,
+					"use_iptables", false);
+
+	if (use_iptables) {
+		torture_unblock_cleanup(tctx);
+	}
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
-- 
2.20.1


From e4f4e4241c27aaad01abd6da302d22dc4bbf2867 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 19:42:18 +0000
Subject: [PATCH 13/19] s4-torture: Add oplock break retry tests - test1

Test to confirm that server sends oplock breaks as expected.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 220 ++++++++++++++++++++++++++++
 1 file changed, 220 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 831a94878e5..13c739e771b 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -28,8 +28,11 @@
 #include "librpc/gen_ndr/ndr_security.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "../libcli/smb/smbXcli_base.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/security/security.h"
 #include "libcli/resolve/resolve.h"
 #include "lib/param/param.h"
+#include "lib/events/events.h"
 #include "oplock_break_handler.h"
 #include "torture/smb2/block.h"
 
@@ -301,6 +304,45 @@ static void test_multichannel_free_channels(struct smb2_tree *tree2A,
 	TALLOC_FREE(tree2C);
 }
 
+static bool test_multichannel_initial_checks(struct torture_context *tctx,
+					     struct smb2_tree *tree1)
+{
+	struct smb2_transport *transport1 = tree1->session->transport;
+	uint32_t server_capabilities;
+	struct fsctl_net_iface_info info;
+
+	if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
+		torture_skip_goto(tctx, fail,
+				  "SMB 3.X Dialect family required for "
+				  "Multichannel tests\n");
+	}
+
+	server_capabilities = smb2cli_conn_server_capabilities(
+					tree1->session->transport->conn);
+	if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
+		torture_skip_goto(tctx, fail,
+			     "Server does not support multichannel.");
+	}
+
+	torture_assert(tctx,
+		test_ioctl_network_interface_info(tctx, tree1, &info),
+		"failed to retrieve network interface info");
+
+	return true;
+fail:
+	return false;
+}
+
+static void test_multichannel_init_smb_create(struct smb2_create *io)
+{
+	io->in.durable_open = false;
+	io->in.durable_open_v2 = true;
+	io->in.persistent_open = false;
+	io->in.create_guid = GUID_random();
+	io->in.timeout = 0x493E0; /* 300000 */
+	/* windows 2016 returns 300000 0x493E0 */
+}
+
 /*
  * We simulate blocking incoming oplock break requests by simply ignoring
  * the incoming break requests.
@@ -396,12 +438,190 @@ static void test_cleanup_blocked_channels(struct torture_context *tctx)
 	}
 }
 
+/*
+ * Oplock break - Test 1
+ * Test to confirm that server sends oplock breaks as expected.
+ * open file1 in session 2A
+ * open file2 in session 2B
+ * open file1 in session 1
+ *      oplock break received
+ * open file1 in session 1
+ *      oplock break received
+ * Cleanup
+ */
+static bool test_multichannel_oplock_break_test1(struct torture_context *tctx,
+					   struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client1_file2 = { 0 };
+	struct smb2_handle h_client1_file3 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_handle h_client2_file2 = { 0 };
+	struct smb2_handle h_client2_file3 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\oplock_break_test1.dat";
+	const char *fname2 = BASEDIR "\\oplock_break_test2.dat";
+	const char *fname3 = BASEDIR "\\oplock_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Oplock break retry: Test1\n");
+
+	torture_reset_break_info(tctx, &break_info);
+
+	transport1->oplock.handler = torture_oplock_ack_handler;
+	transport1->oplock.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(break_info.count, 0);
+
+	smb2_oplock_create_share(&io1, fname1,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_oplock_create_share(&io2, fname2,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_oplock_create_share(&io3, fname3,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, NULL);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+
+	/* 2a opens file1 */
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+	/* 2b opens file2 */
+	torture_comment(tctx, "client2 opens fname2 via session 2B\n");
+	status = smb2_create(tree2B, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+
+	/* 1 opens file1 - batchoplock break? */
+	torture_comment(tctx, "client1 opens fname1 via session 1\n");
+	io1.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+
+	torture_reset_break_info(tctx, &break_info);
+
+	/* 1 opens file2 - batchoplock break? */
+	torture_comment(tctx, "client1 opens fname2 via session 1\n");
+	io2.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+
+	/* cleanup everything */
+	torture_reset_break_info(tctx, &break_info);
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	smb2_util_close(tree2A, h_client2_file1);
+	smb2_util_close(tree2A, h_client2_file2);
+	smb2_util_close(tree2A, h_client2_file3);
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(break_info.count, 0);
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	tree2A = tree2B = tree2C = NULL;
+done:
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+		smb2_util_close(tree2A, h_client2_file2);
+		smb2_util_close(tree2A, h_client2_file3);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
+	struct torture_suite *suite_generic = torture_suite_create(ctx,
+								   "generic");
+	struct torture_suite *suite_oplocks = torture_suite_create(ctx,
+								   "oplocks");
+
+	torture_suite_add_suite(suite, suite_generic);
+	torture_suite_add_suite(suite, suite_oplocks);
 
 	torture_suite_add_1smb2_test(suite, "interface_info",
 				     test_multichannel_interface_info);
+	torture_suite_add_1smb2_test(suite_oplocks, "test1",
+				     test_multichannel_oplock_break_test1);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From 820532c0ef431c4f31dcdb42f47c70ad3dfbd177 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 20:02:33 +0000
Subject: [PATCH 14/19] s4-torture: Add oplock break retry tests - test2

Test to see if oplock break retries are sent by the server.
Also checks to see if new channels can be created and used
after an oplock break retry.

The test by default blocks channels by ignoring incoming lease break
requests on that channel. This does not work when testing against a
windows server.
Use --option=torture:use_iptables=true to use iptables to block ports
instead when testing against windows servers.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 256 ++++++++++++++++++++++++++++
 1 file changed, 256 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 13c739e771b..222ea8b3107 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -607,6 +607,260 @@ done:
 	return ret;
 }
 
+/*
+ * Oplock Break Test 2
+ * Test to see if oplock break retries are sent by the server.
+ * Also checks to see if new channels can be created and used
+ * after an oplock break retry.
+ * open file1 in 2A
+ * open file2 in 2B
+ * open file1 in session 1
+ *      oplock break received
+ * block channel on which oplock break received
+ * open file2 in session 1
+ *      oplock break not received. Retry received.
+ *      file opened
+ * write to file2 on 2B
+ *      Break sent to session 1(which has file2 open)
+ *      Break sent to session 2A(which has read oplock)
+ * close file1 in session 1
+ * open file1 with session 1
+ * unblock blocked channel
+ * disconnect blocked channel
+ * connect channel 2D
+ * open file3 in 2D
+ * open file3 in session 1
+ *      receive break
+ */
+static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
+					   struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client1_file2 = { 0 };
+	struct smb2_handle h_client1_file3 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_handle h_client2_file2 = { 0 };
+	struct smb2_handle h_client2_file3 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\oplock_break_test1.dat";
+	const char *fname2 = BASEDIR "\\oplock_break_test2.dat";
+	const char *fname3 = BASEDIR "\\oplock_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_tree *tree2D = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport *transport2 = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+	DATA_BLOB blob;
+	bool block_ok = false;
+	bool unblock_ok = false;
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Oplock break retry: Test2\n");
+
+	torture_reset_break_info(tctx, &break_info);
+
+	transport1->oplock.handler = torture_oplock_ack_handler;
+	transport1->oplock.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(break_info.count, 0);
+
+	smb2_oplock_create_share(&io1, fname1,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_oplock_create_share(&io2, fname2,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_oplock_create_share(&io3, fname3,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, &tree2C);
+	torture_assert(tctx, ret, "Could not create channels.\n")
+
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	io1.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+
+	torture_comment(tctx, "client2 opens fname2 via session 2B\n");
+	io2.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree2B, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+
+	torture_comment(tctx, "client1 opens fname1 via session 1\n");
+	io1.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+
+	/* We use the transport over which this oplock break was received */
+	transport2 = break_info.received_transport;
+	torture_reset_break_info(tctx, &break_info);
+
+	/* block channel */
+	block_ok = test_block_channel(tctx, transport2);
+
+	torture_comment(tctx, "client1 opens fname2 via session 1\n");
+	io2.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
+
+	/*
+	 * Samba downgrades oplock to a level 2 oplock.
+	 * Windows 2016 revokes oplock
+	 */
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+	torture_reset_break_info(tctx, &break_info);
+
+	torture_comment(tctx, "Trying write to file2 on tree2B\n");
+
+	blob = data_blob_string_const("Here I am");
+	status = smb2_util_write(tree2B,
+				 h_client2_file2,
+				 blob.data,
+				 0,
+				 blob.length);
+	torture_assert_ntstatus_ok(tctx, status,
+		"failed to write file2 via channel 2B");
+
+	/*
+	 * Samba: Write triggers 2 oplock breaks
+	 *  for session 1 which has file2 open
+	 *  for session 2 which has type 2 oplock
+	 * Windows 2016: Only one oplock break for session 1
+	 */
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL_GREATER_THAN(break_info.count, 0);
+	torture_reset_break_info(tctx, &break_info);
+
+	torture_comment(tctx, "client1 closes fname2 via session 1\n");
+	smb2_util_close(tree1, h_client1_file2);
+
+	torture_comment(tctx, "client1 opens fname2 via session 1 again\n");
+	io2.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	io2.out.alloc_size = 0;
+	io2.out.size = 0;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
+
+	/*
+	 * now add a fourth channel and repeat the test, we need to reestablish
+	 * transport2 because the remote end has invalidated our connection
+	 */
+	torture_comment(tctx, "Connecting session 2D\n");
+	tree2D = test_multichannel_create_channel(tctx, host, share,
+				     credentials, &transport2_options, tree2B);
+	if (!tree2D) {
+		goto done;
+	}
+
+	torture_reset_break_info(tctx, &break_info);
+	torture_comment(tctx, "client 2 opening fname3 over transport2D\n");
+	status = smb2_create(tree2D, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+	torture_comment(tctx, "client1 opens fname3 via session 1\n");
+	status = smb2_create(tree1, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("s"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+
+done:
+	if (block_ok && !unblock_ok) {
+		test_unblock_channel(tctx, transport2);
+	}
+	test_cleanup_blocked_channels(tctx);
+
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	if (tree2B != NULL) {
+		smb2_util_close(tree2B, h_client2_file1);
+		smb2_util_close(tree2B, h_client2_file2);
+		smb2_util_close(tree2B, h_client2_file3);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	if (tree2D != NULL) {
+		TALLOC_FREE(tree2D);
+	}
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -622,6 +876,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_interface_info);
 	torture_suite_add_1smb2_test(suite_oplocks, "test1",
 				     test_multichannel_oplock_break_test1);
+	torture_suite_add_1smb2_test(suite_oplocks, "test2",
+				     test_multichannel_oplock_break_test2);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From 444e56fea855ca6b8bd9a7bcafaef0ce47c3e9ed Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 20:05:02 +0000
Subject: [PATCH 15/19] s4-torture: Add lease break retry tests - test1

Test to check if lease breaks are sent by the server as expected.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 219 ++++++++++++++++++++++++++++
 1 file changed, 219 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 222ea8b3107..cd0e2c508a5 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -34,6 +34,7 @@
 #include "lib/param/param.h"
 #include "lib/events/events.h"
 #include "oplock_break_handler.h"
+#include "lease_break_handler.h"
 #include "torture/smb2/block.h"
 
 #define BASEDIR "multichanneltestdir"
@@ -861,6 +862,219 @@ done:
 	return ret;
 }
 
+static const uint64_t LEASE1F1 = 0xBADC0FFEE0DDF00Dull;
+static const uint64_t LEASE1F2 = 0xBADC0FFEE0DDD00Dull;
+static const uint64_t LEASE1F3 = 0xDADC0FFEE0DDD00Dull;
+static const uint64_t LEASE2F1 = 0xDEADBEEFFEEDBEADull;
+static const uint64_t LEASE2F2 = 0xDAD0FFEDD00DF00Dull;
+static const uint64_t LEASE2F3 = 0xBAD0FFEDD00DF00Dull;
+
+/*
+ * Lease Break Test 1:
+ * Test to check if lease breaks are sent by the server as expected.
+ *      open file1 in session 2A
+ *      open file2 in session 2B
+ *      open file3 in session 2C
+ *      open file1 in session 1
+ *           lease break sent
+ *      open file2 in session 1
+ *           lease break sent
+ *      open file3 in session 1
+ *           lease break sent
+ */
+static bool test_multichannel_lease_break_test1(struct torture_context *tctx,
+						struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client1_file2 = { 0 };
+	struct smb2_handle h_client1_file3 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_handle h_client2_file2 = { 0 };
+	struct smb2_handle h_client2_file3 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\lease_break_test1.dat";
+	const char *fname2 = BASEDIR "\\lease_break_test2.dat";
+	const char *fname3 = BASEDIR "\\lease_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport *transport2A = NULL;
+	struct smb2_transport *transport2B = NULL;
+	struct smb2_transport *transport2C = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_lease ls3;
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Lease break retry: Test1\n");
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	transport1->lease.handler = torture_lease_handler;
+	transport1->lease.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, &tree2C);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+	transport2A = tree2A->session->transport;
+	transport2B = tree2B->session->transport;
+	transport2C = tree2C->session->transport;
+
+	/* 2a opens file1 */
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* 2b opens file2 */
+	torture_comment(tctx, "client2 opens fname2 via session 2B\n");
+	status = smb2_create(tree2B, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io2, "RHW", true, LEASE2F2, 0);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* 2c opens file3 */
+	torture_comment(tctx, "client2 opens fname3 via session 2C\n");
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2C, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io3, "RHW", true, LEASE2F3, 0);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* 1 opens file1 - lease break? */
+	torture_comment(tctx, "client1 opens fname1 via session 1\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
+	CHECK_VAL(lease_break_info.count, 1);
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/* 1 opens file2 - lease break? */
+	torture_comment(tctx, "client1 opens fname2 via session 1\n");
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE1F2,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io2, "RH", true, LEASE1F2, 0);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F2);
+	CHECK_VAL(lease_break_info.count, 1);
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/* 1 opens file3 - lease break? */
+	torture_comment(tctx, "client1 opens fname3 via session 1\n");
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE1F3,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io3, "RH", true, LEASE1F3, 0);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F3);
+	CHECK_VAL(lease_break_info.count, 1);
+
+	/* cleanup everything */
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	smb2_util_close(tree2A, h_client2_file1);
+	smb2_util_close(tree2A, h_client2_file2);
+	smb2_util_close(tree2A, h_client2_file3);
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(lease_break_info.count, 0);
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	tree2A = tree2B = tree2C = NULL;
+done:
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+		smb2_util_close(tree2A, h_client2_file2);
+		smb2_util_close(tree2A, h_client2_file3);
+	}
+
+	if (h != NULL) {
+		smb2_util_close(tree1, *h);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -868,9 +1082,12 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 								   "generic");
 	struct torture_suite *suite_oplocks = torture_suite_create(ctx,
 								   "oplocks");
+	struct torture_suite *suite_leases = torture_suite_create(ctx,
+								  "leases");
 
 	torture_suite_add_suite(suite, suite_generic);
 	torture_suite_add_suite(suite, suite_oplocks);
+	torture_suite_add_suite(suite, suite_leases);
 
 	torture_suite_add_1smb2_test(suite, "interface_info",
 				     test_multichannel_interface_info);
@@ -878,6 +1095,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_oplock_break_test1);
 	torture_suite_add_1smb2_test(suite_oplocks, "test2",
 				     test_multichannel_oplock_break_test2);
+	torture_suite_add_1smb2_test(suite_leases, "test1",
+				     test_multichannel_lease_break_test1);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From 9cba99406c2fe05ff9241103c68bb9428ef2d755 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 14:34:29 +0000
Subject: [PATCH 16/19] s4-torture: Add lease break retry tests - test2

Test to check if lease breaks are sent by the server as expected.

The test by default blocks channels by ignoring incoming lease break
requests on that channel. This does not work when testing against a
windows server.
Use --option=torture:use_iptables=true to use iptables to block ports
instead when testing against windows servers.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 314 ++++++++++++++++++++++++++++
 1 file changed, 314 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index cd0e2c508a5..f8eb68cefc4 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -1075,6 +1075,318 @@ done:
 	return ret;
 }
 
+/*
+ * Lease Break Test 2:
+ * Test for lease break retries being sent by the server.
+ *      Connect 2A, 2B
+ *      open file1 in session 2A
+ *      open file2 in session 2B
+ *      block 2A
+ *      open file2 in session 1
+ *           lease break retry reaches the client?
+ *      Connect 2C
+ *      open file3 in session 2C
+ *      unblock 2A
+ *      open file1 in session 1
+ *           lease break reaches the client?
+ *      open file3 in session 1
+ *           lease break reached the client?
+ *      Cleanup
+ *           On deletion by 1, lease breaks sent for file1, file2 and file3
+ *           on 2B
+ *           This changes RH lease to R for Session 2.
+ *           (This has been disabled while we add support for sending lease
+ *            break for handle leases.)
+ */
+static bool test_multichannel_lease_break_test2(struct torture_context *tctx,
+						struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client1_file2 = { 0 };
+	struct smb2_handle h_client1_file3 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_handle h_client2_file2 = { 0 };
+	struct smb2_handle h_client2_file3 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\lease_break_test1.dat";
+	const char *fname2 = BASEDIR "\\lease_break_test2.dat";
+	const char *fname3 = BASEDIR "\\lease_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport *transport2A = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_lease ls3;
+	bool block_ok = false;
+	bool unblock_ok = false;
+
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Lease break retry: Test2\n");
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	transport1->lease.handler = torture_lease_handler;
+	transport1->lease.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, NULL);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+	transport2A = tree2A->session->transport;
+
+	/* 2a opens file1 */
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
+	CHECK_VAL(io1.out.durable_open_v2, false); //true);
+	CHECK_VAL(io1.out.timeout, io1.in.timeout);
+	CHECK_VAL(io1.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* 2b opens file2 */
+	torture_comment(tctx, "client2 opens fname2 via session 2B\n");
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2B, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io2, "RHW", true, LEASE2F2, 0);
+	CHECK_VAL(io2.out.durable_open_v2, false); //true);
+	CHECK_VAL(io2.out.timeout, io2.in.timeout);
+	CHECK_VAL(io2.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+
+	torture_comment(tctx, "Blocking 2A\n");
+	/* Block 2A */
+	block_ok = test_block_channel(tctx, transport2A);
+	torture_assert(tctx, block_ok, "we could not block tcp transport");
+
+	/* 1 opens file2 */
+	torture_comment(tctx,
+			"Client opens fname2 with session1 with 2A blocked\n");
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE1F2,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io2, "RH", true, LEASE1F2, 0);
+	CHECK_VAL(io2.out.durable_open_v2, false);
+	CHECK_VAL(io2.out.timeout, 0);
+	CHECK_VAL(io2.out.durable_open, false);
+
+	if (lease_break_info.count == 0) {
+		torture_comment(tctx,
+				"Did not receive expected lease break!!\n");
+	} else {
+		torture_comment(tctx, "Received %d lease break(s)!!\n",
+				lease_break_info.count);
+	}
+
+	CHECK_VAL(lease_break_info.count, 1);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F2);
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/* Connect 2C */
+	torture_comment(tctx, "Connecting session 2C\n");
+	talloc_free(tree2C);
+	tree2C = test_multichannel_create_channel(tctx, host, share,
+				credentials, &transport2_options, tree2A);
+	if (!tree2C) {
+		goto done;
+	}
+
+	/* 2c opens file3 */
+	torture_comment(tctx, "client2 opens fname3 via session 2C\n");
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2C, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io3, "RHW", true, LEASE2F3, 0);
+	CHECK_VAL(io3.out.durable_open_v2, false);
+	CHECK_VAL(io3.out.timeout, io2.in.timeout);
+	CHECK_VAL(io3.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* Unblock 2A */
+	torture_comment(tctx, "Unblocking 2A\n");
+	unblock_ok = test_unblock_channel(tctx, transport2A);
+	torture_assert(tctx, unblock_ok, "we could not unblock tcp transport");
+
+	/* 1 opens file1 */
+	torture_comment(tctx, "Client opens fname1 with session 1\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
+
+	if (lease_break_info.count == 0) {
+		torture_comment(tctx,
+				"Did not receive expected lease break!!\n");
+	} else {
+		torture_comment(tctx,
+				"Received %d lease break(s)!!\n",
+				lease_break_info.count);
+	}
+	CHECK_VAL(lease_break_info.count, 1);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/*1 opens file3 */
+	torture_comment(tctx, "client opens fname3 via session 1\n");
+
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE1F3,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io3, "RH", true, LEASE1F3, 0);
+
+	if (lease_break_info.count == 0) {
+		torture_comment(tctx,
+				"Did not receive expected lease break!!\n");
+	} else {
+		torture_comment(tctx,
+				"Received %d lease break(s)!!\n",
+				lease_break_info.count);
+	}
+	CHECK_VAL(lease_break_info.count, 1);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F3);
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+
+	/*
+	 * Session 2 still has RW lease on file 1. Deletion of this file by 1
+	 *  leads to a lease break call to session 2 file1
+	 */
+	smb2_util_unlink(tree1, fname1);
+	/*
+	 * Bug - Samba does not revoke Handle lease on unlink
+	 * CHECK_BREAK_INFO("RH", "R", LEASE2F1);
+	 */
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/*
+	 * Session 2 still has RW lease on file 2. Deletion of this file by 1
+	 *  leads to a lease break call to session 2 file2
+	 */
+	smb2_util_unlink(tree1, fname2);
+	/*
+	 * Bug - Samba does not revoke Handle lease on unlink
+	 * CHECK_BREAK_INFO("RH", "R", LEASE2F2);
+	 */
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/*
+	 * Session 2 still has RW lease on file 3. Deletion of this file by 1
+	 *  leads to a lease break call to session 2 file3
+	 */
+	smb2_util_unlink(tree1, fname3);
+	/*
+	 * Bug - Samba does not revoke Handle lease on unlink
+	 * CHECK_BREAK_INFO("RH", "R", LEASE2F3);
+	 */
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	smb2_util_close(tree2C, h_client2_file1);
+	smb2_util_close(tree2C, h_client2_file2);
+	smb2_util_close(tree2C, h_client2_file3);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	tree2A = tree2B = tree2C = NULL;
+
+done:
+	if (block_ok && !unblock_ok) {
+		test_unblock_channel(tctx, transport2A);
+	}
+	test_cleanup_blocked_channels(tctx);
+
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+		smb2_util_close(tree2A, h_client2_file2);
+		smb2_util_close(tree2A, h_client2_file3);
+	}
+
+	if (h != NULL) {
+		smb2_util_close(tree1, *h);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -1097,6 +1409,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_oplock_break_test2);
 	torture_suite_add_1smb2_test(suite_leases, "test1",
 				     test_multichannel_lease_break_test1);
+	torture_suite_add_1smb2_test(suite_leases, "test2",
+				     test_multichannel_lease_break_test2);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From 0ecbd27df74d063aa8a7bd8b4f4396817d71c825 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 14:38:13 +0000
Subject: [PATCH 17/19] s4-torture: Add lease break retry tests - test3

Check to see how the server behaves if lease break response is sent
over a different channel to one over which the break is received.

The test by default blocks channels by ignoring incoming lease break
requests on that channel. This does not work when testing against a
windows server.
Use --option=torture:use_iptables=true to use iptables to block ports
instead when testing against windows servers.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 151 ++++++++++++++++++++++++++++
 1 file changed, 151 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index f8eb68cefc4..f8a4387c459 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -439,6 +439,16 @@ static void test_cleanup_blocked_channels(struct torture_context *tctx)
 	}
 }
 
+/* Timer handler function notifies the registering function that time is up */
+static void timeout_cb(struct tevent_context *ev,
+		       struct tevent_timer *te,
+		       struct timeval current_time,
+		       void *private_data)
+{
+	bool *timesup = (bool *)private_data;
+	*timesup = true;
+}
+
 /*
  * Oplock break - Test 1
  * Test to confirm that server sends oplock breaks as expected.
@@ -1387,6 +1397,145 @@ done:
 	return ret;
 }
 
+/*
+ * Test 3: Check to see how the server behaves if lease break
+ *      response is sent over a different channel to one over which
+ *      the break is received.
+ *      Connect 2A, 2B
+ *      open file1 in session 2A
+ *      open file1 in session 1
+ *           Lease break sent to 2A
+ *           2B sends back lease break reply.
+ *      session 1 allowed to open file
+ */
+static bool test_multichannel_lease_break_test3(struct torture_context *tctx,
+						struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_create io1;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\lease_break_test1.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport *transport2A = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+	struct smb2_lease ls1;
+	struct tevent_timer *te = NULL;
+	struct timeval ne;
+	bool timesup = false;
+	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Lease break retry: Test3\n");
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	transport1->lease.handler = torture_lease_handler;
+	transport1->lease.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io1);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						credentials,
+						&transport2_options,
+						&tree2A, &tree2B, NULL);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+	transport2A = tree2A->session->transport;
+	transport2A->lease.private_data = tree2B;
+
+	/* 2a opens file1 */
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
+	CHECK_VAL(io1.out.durable_open_v2, false); //true);
+	CHECK_VAL(io1.out.timeout, io1.in.timeout);
+	CHECK_VAL(io1.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* Set a timeout for 5 seconds for session 1 to open file1 */
+	ne = tevent_timeval_current_ofs(0, 5000000);
+	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
+	if (te == NULL) {
+		torture_comment(tctx, "Failed to add timer.");
+		goto done;
+	}
+
+	/* 1 opens file2 */
+	torture_comment(tctx, "Client opens fname1 with session 1\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
+	CHECK_VAL(io1.out.durable_open_v2, false);
+	CHECK_VAL(io1.out.timeout, 0);
+	CHECK_VAL(io1.out.durable_open, false);
+
+	CHECK_VAL(lease_break_info.count, 1);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
+
+	/*
+	 * Check if timeout handler was fired. This would indicate
+	 * that the server didn't receive a reply for the oplock break
+	 * from the client and the server let session 1 open the file
+	 * only after the oplock break timeout.
+	 */
+	CHECK_VAL(timesup, false);
+
+done:
+	smb2_util_close(tree1, h_client1_file1);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+	}
+
+	if (h != NULL) {
+		smb2_util_close(tree1, *h);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, NULL);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -1411,6 +1560,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_lease_break_test1);
 	torture_suite_add_1smb2_test(suite_leases, "test2",
 				     test_multichannel_lease_break_test2);
+	torture_suite_add_1smb2_test(suite_leases, "test3",
+				     test_multichannel_lease_break_test3);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From fe65d27f40d4e8dfb2c6b05c0c425f7f99ec3c3d Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 14:47:58 +0000
Subject: [PATCH 18/19] s4-torture: Add lease break retry tests - test4

Test to see how the server behaves when the client flushes data back to
the server but doesn't send the lease break response over the channel.
Does it then retry the lease break?

This test is specifically expected to run against Samba and will not
work against a MS Windows servers because it uses the ignore method to
ignore oplock breaks sent by the server.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 196 ++++++++++++++++++++++++++++
 1 file changed, 196 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index f8a4387c459..00cb61f1dc5 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -1536,6 +1536,200 @@ done:
 	return ret;
 }
 
+/* lease handler for test4 */
+static bool test4_lease_break_handler(struct smb2_transport *transport,
+			   const struct smb2_lease_break *lb,
+			   void *private_data)
+{
+	NTSTATUS status;
+	bool ret = true;
+	const char *fname = BASEDIR "\\lease_break_test1.dat";
+	struct smb2_tree *test4_tree2 = private_data;
+	TALLOC_CTX *mem_ctx = test4_tree2->session->transport->ev;
+	struct torture_context *tctx = lease_break_info.tctx;
+	struct smb2_handle test4_h_file = lease_break_info.oplock_handle;
+	DATA_BLOB blob;
+
+	lease_break_info.lease_transport = transport;
+	lease_break_info.lease_break = *lb;
+	lease_break_info.count++;
+
+	torture_comment(tctx, "Test 6 Lease break handler called.\n");
+	torture_comment(tctx, "Trying write to file using given session.\n");
+
+	blob = data_blob_string_const("Here I am");
+	status = smb2_util_write(test4_tree2,
+				 test4_h_file,
+				 blob.data,
+				 0,
+				 blob.length);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+		"failed to write file");
+
+done:
+	return ret;
+}
+
+/*
+ * Lease Break Test 4:
+ * Test to see how the server behaves when the client flushes data back to the
+ * server but doesn't send the lease break response over the channel. Does it
+ * then retry the lease break?
+ *      Connect 2A, 2B
+ *      open file1 in session 2A
+ *      set lease handler for 2A to ignore break requests
+ *      open file1 in session 1
+ *           Lease break sent to 2A
+ *           Write to file in 2A
+ *           Do not send ack to lease break
+ *      Check to see if second lease break sent
+ *      Cleanup
+ */
+static bool test_multichannel_lease_break_test4(struct torture_context *tctx,
+						struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client1_file2 = { 0 };
+	struct smb2_handle h_client1_file3 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_handle h_client2_file2 = { 0 };
+	struct smb2_handle h_client2_file3 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\lease_break_test1.dat";
+	const char *fname2 = BASEDIR "\\lease_break_test2.dat";
+	const char *fname3 = BASEDIR "\\lease_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport *transport2A = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_lease ls3;
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Lease break retry: Test4\n");
+	torture_comment(tctx, "This test is specifically expected to run "
+			"against samba and will not work against windows "
+			"servers. The windows server assumes that if the "
+			"send() command returns successfully, the lease break "
+			"has been delivered. In this test, we rely on the "
+			"Samba behaviour of waiting for a reply for the lease "
+			"break from the server instead.\n");
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	transport1->lease.handler = torture_lease_handler;
+	transport1->lease.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, NULL);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+	transport2A = tree2A->session->transport;
+
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
+	CHECK_VAL(io1.out.durable_open_v2, false); //true);
+	CHECK_VAL(io1.out.timeout, io1.in.timeout);
+	CHECK_VAL(io1.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	torture_comment(tctx, "Blocking 2A\n");
+	/* Set our lease handler for 2A */
+	lease_break_info.oplock_handle = h_client2_file1;
+	transport2A->lease.handler = test4_lease_break_handler;
+
+	torture_comment(tctx,
+			"Client opens fname1 with session 1 with 2A blocked\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
+
+	if (lease_break_info.count == 0) {
+		torture_comment(tctx,
+				"Did not receive expected lease break!!\n");
+	} else {
+		torture_comment(tctx,
+				"Received %d lease break(s)!!\n",
+				lease_break_info.count);
+	}
+
+	/* Should receive 2 lease breaks */
+	CHECK_VAL(lease_break_info.count, 2);
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+done:
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+	}
+
+	if (h != NULL) {
+		smb2_util_close(tree1, *h);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -1562,6 +1756,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_lease_break_test2);
 	torture_suite_add_1smb2_test(suite_leases, "test3",
 				     test_multichannel_lease_break_test3);
+	torture_suite_add_1smb2_test(suite_leases, "test4",
+				     test_multichannel_lease_break_test4);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From a3d33e737e59b6452783db1f1bc2facac2876b22 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 20:07:09 +0000
Subject: [PATCH 19/19] s4-torture: add test to check for max. number of
 channels per session.

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/multichannel.c | 115 ++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 00cb61f1dc5..a2ebe47d1e9 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -1730,6 +1730,119 @@ done:
 	return ret;
 }
 
+/*
+ * Test limits of channels
+ */
+static bool test_multichannel_num_channels(struct torture_context *tctx,
+					   struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	bool ret = true;
+	struct smb2_tree **tree2 = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport **transport2 = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session **session2 = NULL;
+	uint32_t server_capabilities;
+	int i;
+	int max_channels = 33; /* 32 is the W2K12R2 and W2K16 limit */
+
+	if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
+		torture_fail(tctx,
+			     "SMB 3.X Dialect family required for Multichannel"
+			     " tests\n");
+	}
+
+	server_capabilities = smb2cli_conn_server_capabilities(
+					tree1->session->transport->conn);
+	if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
+		torture_fail(tctx,
+			     "Server does not support multichannel.");
+	}
+
+	torture_comment(tctx, "Testing max. number of channels\n");
+
+	transport2_options = transport1->options;
+	transport2_options.client_guid = GUID_random();
+
+	tree2		= talloc_zero_array(mem_ctx, struct smb2_tree *,
+					    max_channels);
+	transport2	= talloc_zero_array(mem_ctx, struct smb2_transport *,
+					    max_channels);
+	session2	= talloc_zero_array(mem_ctx, struct smb2_session *,
+					    max_channels);
+	if (tree2 == NULL || transport2 == NULL || session2 == NULL) {
+		torture_fail(tctx, "out of memory");
+	}
+
+	for (i = 0; i < max_channels; i++) {
+
+		NTSTATUS expected_status;
+
+		torture_assert_ntstatus_ok_goto(tctx,
+			smb2_connect(tctx,
+				host,
+				lpcfg_smb_ports(tctx->lp_ctx),
+				share,
+				lpcfg_resolve_context(tctx->lp_ctx),
+				credentials,
+				&tree2[i],
+				tctx->ev,
+				&transport2_options,
+				lpcfg_socket_options(tctx->lp_ctx),
+				lpcfg_gensec_settings(tctx, tctx->lp_ctx)
+				),
+			ret, done, "smb2_connect failed");
+
+		transport2[i] = tree2[i]->session->transport;
+
+		if (i == 0) {
+			/* done for the 1st channel */
+			continue;
+		}
+
+		/*
+		 * Now bind the session2[i] to the transport2
+		 */
+		session2[i] = smb2_session_channel(transport2[i],
+						   lpcfg_gensec_settings(tctx,
+								 tctx->lp_ctx),
+						   tree2[0],
+						   tree2[0]->session);
+
+		torture_assert(tctx, session2[i] != NULL,
+			       "smb2_session_channel failed");
+
+		torture_comment(tctx, "established transport2 [#%d]\n", i);
+
+		if (i >= 32) {
+			expected_status = NT_STATUS_INSUFFICIENT_RESOURCES;
+		} else {
+			expected_status = NT_STATUS_OK;
+		}
+
+		torture_assert_ntstatus_equal_goto(tctx,
+			smb2_session_setup_spnego(session2[i],
+				popt_get_cmdline_credentials(),
+				0 /* previous_session_id */),
+			expected_status,
+			ret, done,
+			talloc_asprintf(tctx, "failed to establish session "
+					      "setup for channel #%d", i));
+
+		torture_comment(tctx, "bound session2 [#%d] to session2 [0]\n",
+				i);
+	}
+
+ done:
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -1746,6 +1859,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 
 	torture_suite_add_1smb2_test(suite, "interface_info",
 				     test_multichannel_interface_info);
+	torture_suite_add_1smb2_test(suite_generic, "num_channels",
+				     test_multichannel_num_channels);
 	torture_suite_add_1smb2_test(suite_oplocks, "test1",
 				     test_multichannel_oplock_break_test1);
 	torture_suite_add_1smb2_test(suite_oplocks, "test2",
-- 
2.20.1



More information about the samba-technical mailing list