Add multichannel tests to smbtorture

Jeremy Allison jra at samba.org
Wed Mar 27 18:31:49 UTC 2019


On Wed, Mar 27, 2019 at 05:07:36PM +0000, Sachin Prabhu via samba-technical wrote:
> 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.

Oh thanks so much for this Sachin ! It is greatly
appreciated.

I'll try and make some time to review.

Guenther, are you able to be second Team reviewer here ?

Cheers,

Jeremy.

> Sachin Prabhu

> 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