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, ×up))
> - == 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, ×up);
> + 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, ×up);
> - 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, ×up);
> 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, ×up);
> 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, ×up);
> + 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