diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 4a092cc5a3936..03f34ec63e10d 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -734,6 +734,7 @@ struct TCP_Server_Info { */ #define CIFS_SERVER_IS_CHAN(server) (!!(server)->primary_server) struct TCP_Server_Info *primary_server; + __u16 channel_sequence_num; /* incremented on primary channel on each chan reconnect */ #ifdef CONFIG_CIFS_SWN_UPCALL bool use_swn_dstaddr; diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 9cd282960c0bb..57da4f23c1e43 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -1725,6 +1725,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); tcp_ses->session_estab = false; tcp_ses->sequence_number = 0; + tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */ tcp_ses->reconnect_instance = 1; tcp_ses->lstrp = jiffies; tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression); diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 6b020d80bb949..1387d5126f53b 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -167,8 +167,17 @@ smb2_set_credits(struct TCP_Server_Info *server, const int val) spin_lock(&server->req_lock); server->credits = val; - if (val == 1) + if (val == 1) { server->reconnect_instance++; + /* + * ChannelSequence updated for all channels in primary channel so that consistent + * across SMB3 requests sent on any channel. See MS-SMB2 3.2.4.1 and 3.2.7.1 + */ + if (CIFS_SERVER_IS_CHAN(server)) + server->primary_server->channel_sequence_num++; + else + server->channel_sequence_num++; + } scredits = server->credits; in_flight = server->in_flight; spin_unlock(&server->req_lock); diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index ae17d78f6ba17..847d69d327c2a 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -88,9 +88,20 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, const struct cifs_tcon *tcon, struct TCP_Server_Info *server) { + struct smb3_hdr_req *smb3_hdr; shdr->ProtocolId = SMB2_PROTO_NUMBER; shdr->StructureSize = cpu_to_le16(64); shdr->Command = smb2_cmd; + if (server->dialect >= SMB30_PROT_ID) { + /* After reconnect SMB3 must set ChannelSequence on subsequent reqs */ + smb3_hdr = (struct smb3_hdr_req *)shdr; + /* if primary channel is not set yet, use default channel for chan sequence num */ + if (CIFS_SERVER_IS_CHAN(server)) + smb3_hdr->ChannelSequence = + cpu_to_le16(server->primary_server->channel_sequence_num); + else + smb3_hdr->ChannelSequence = cpu_to_le16(server->channel_sequence_num); + } if (server) { spin_lock(&server->req_lock); /* Request up to 10 credits but don't go over the limit. */ diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index 7d605db3bb3b9..9619015d78f29 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -153,6 +153,28 @@ struct smb2_hdr { __u8 Signature[16]; } __packed; +struct smb3_hdr_req { + __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ + __le16 StructureSize; /* 64 */ + __le16 CreditCharge; /* MBZ */ + __le16 ChannelSequence; /* See MS-SMB2 3.2.4.1 and 3.2.7.1 */ + __le16 Reserved; + __le16 Command; + __le16 CreditRequest; /* CreditResponse */ + __le32 Flags; + __le32 NextCommand; + __le64 MessageId; + union { + struct { + __le32 ProcessId; + __le32 TreeId; + } __packed SyncId; + __le64 AsyncId; + } __packed Id; + __le64 SessionId; + __u8 Signature[16]; +} __packed; + struct smb2_pdu { struct smb2_hdr hdr; __le16 StructureSize2; /* size of wct area (varies, request specific) */