Skip to content

Commit 665e187

Browse files
paliSteve French
authored andcommitted
cifs: Improve handling of NetBIOS packets
Now all NetBIOS session logic is handled in ip_rfc1001_connect() function, so cleanup is_smb_response() function which contains generic handling of incoming SMB packets. Note that function is_smb_response() is not used directly or indirectly (e.g. over cifs_demultiplex_thread() by ip_rfc1001_connect() function. Except the Negative Session Response and the Session Keep Alive packet, the cifs_demultiplex_thread() should not receive any NetBIOS session packets. And Session Keep Alive packet may be received only when the NetBIOS session was established by ip_rfc1001_connect() function. So treat any such packet as error and schedule reconnect. Negative Session Response packet is returned from Windows SMB server (from Windows 98 and also from Windows Server 2022) if client sent over port 139 SMB negotiate request without previously establishing a NetBIOS session. The common scenario is that Negative Session Response packet is returned for the SMB negotiate packet, which is the first one which SMB client sends (if it is not establishing a NetBIOS session). Note that server port 139 may be forwarded and mapped between virtual machines to different number. And Linux SMB client do not call function ip_rfc1001_connect() when prot is not 139. So nowadays when using port mapping or port forwarding between VMs, it is not so uncommon to see this error. Currently the logic on Negative Session Response packet changes server port to 445 and force reconnection. But this logic does not work when using non-standard port numbers and also does not help if the server on specified port is requiring establishing a NetBIOS session. Fix this Negative Session Response logic and instead of changing server port (on which server does not have to listen), force reconnection with establishing a NetBIOS session. Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 7d14dd6 commit 665e187

File tree

3 files changed

+128
-18
lines changed

3 files changed

+128
-18
lines changed

fs/smb/client/cifsglob.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ struct TCP_Server_Info {
715715
__u64 conn_id; /* connection identifier (useful for debugging) */
716716
int srv_count; /* reference counter */
717717
int rfc1001_sessinit; /* whether to estasblish netbios session */
718+
bool with_rfc1001; /* if netbios session is used */
718719
/* 15 character server name + 0x20 16th byte indicating type = srv */
719720
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
720721
struct smb_version_operations *ops;
@@ -1720,6 +1721,7 @@ struct mid_q_entry {
17201721
void *resp_buf; /* pointer to received SMB header */
17211722
unsigned int resp_buf_size;
17221723
int mid_state; /* wish this were enum but can not pass to wait_event */
1724+
int mid_rc; /* rc for MID_RC */
17231725
unsigned int mid_flags;
17241726
__le16 command; /* smb command code */
17251727
unsigned int optype; /* operation type */
@@ -1882,6 +1884,7 @@ static inline bool is_replayable_error(int error)
18821884
#define MID_RESPONSE_MALFORMED 0x10
18831885
#define MID_SHUTDOWN 0x20
18841886
#define MID_RESPONSE_READY 0x40 /* ready for other process handle the rsp */
1887+
#define MID_RC 0x80 /* mid_rc contains custom rc */
18851888

18861889
/* Flags */
18871890
#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */

fs/smb/client/connect.c

Lines changed: 122 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
371371
*
372372
*/
373373
static int __cifs_reconnect(struct TCP_Server_Info *server,
374-
bool mark_smb_session)
374+
bool mark_smb_session, bool once)
375375
{
376376
int rc = 0;
377377

@@ -399,6 +399,9 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
399399
if (rc) {
400400
cifs_server_unlock(server);
401401
cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
402+
/* If was asked to reconnect only once, do not try it more times */
403+
if (once)
404+
break;
402405
msleep(3000);
403406
} else {
404407
atomic_inc(&tcpSesReconnectCount);
@@ -564,19 +567,33 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
564567
return rc;
565568
}
566569

567-
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
570+
static int
571+
_cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session, bool once)
568572
{
569573
if (!server->leaf_fullpath)
570-
return __cifs_reconnect(server, mark_smb_session);
574+
return __cifs_reconnect(server, mark_smb_session, once);
571575
return reconnect_dfs_server(server);
572576
}
573577
#else
574-
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
578+
static int
579+
_cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session, bool once)
575580
{
576-
return __cifs_reconnect(server, mark_smb_session);
581+
return __cifs_reconnect(server, mark_smb_session, once);
577582
}
578583
#endif
579584

585+
int
586+
cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
587+
{
588+
return _cifs_reconnect(server, mark_smb_session, false);
589+
}
590+
591+
static int
592+
cifs_reconnect_once(struct TCP_Server_Info *server)
593+
{
594+
return _cifs_reconnect(server, true, true);
595+
}
596+
580597
static void
581598
cifs_echo_request(struct work_struct *work)
582599
{
@@ -803,26 +820,110 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
803820
/* Regular SMB response */
804821
return true;
805822
case RFC1002_SESSION_KEEP_ALIVE:
823+
/*
824+
* RFC 1002 session keep alive can sent by the server only when
825+
* we established a RFC 1002 session. But Samba servers send
826+
* RFC 1002 session keep alive also over port 445 on which
827+
* RFC 1002 session is not established.
828+
*/
806829
cifs_dbg(FYI, "RFC 1002 session keep alive\n");
807830
break;
808831
case RFC1002_POSITIVE_SESSION_RESPONSE:
809-
cifs_dbg(FYI, "RFC 1002 positive session response\n");
832+
/*
833+
* RFC 1002 positive session response cannot be returned
834+
* for SMB request. RFC 1002 session response is handled
835+
* exclusively in ip_rfc1001_connect() function.
836+
*/
837+
cifs_server_dbg(VFS, "RFC 1002 positive session response (unexpected)\n");
838+
cifs_reconnect(server, true);
810839
break;
811840
case RFC1002_NEGATIVE_SESSION_RESPONSE:
812841
/*
813842
* We get this from Windows 98 instead of an error on
814-
* SMB negprot response.
815-
*/
816-
cifs_dbg(FYI, "RFC 1002 negative session response\n");
817-
/* give server a second to clean up */
818-
msleep(1000);
819-
/*
820-
* Always try 445 first on reconnect since we get NACK
821-
* on some if we ever connected to port 139 (the NACK
822-
* is since we do not begin with RFC1001 session
823-
* initialize frame).
843+
* SMB negprot response, when we have not established
844+
* RFC 1002 session (which means ip_rfc1001_connect()
845+
* was skipped). Note that same still happens with
846+
* Windows Server 2022 when connecting via port 139.
847+
* So for this case when mount option -o nonbsessinit
848+
* was not specified, try to reconnect with establishing
849+
* RFC 1002 session. If new socket establishment with
850+
* RFC 1002 session was successful then return to the
851+
* mid's caller -EAGAIN, so it can retry the request.
824852
*/
825-
cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
853+
if (!cifs_rdma_enabled(server) &&
854+
server->tcpStatus == CifsInNegotiate &&
855+
!server->with_rfc1001 &&
856+
server->rfc1001_sessinit != 0) {
857+
int rc, mid_rc;
858+
struct mid_q_entry *mid, *nmid;
859+
LIST_HEAD(dispose_list);
860+
861+
cifs_dbg(FYI, "RFC 1002 negative session response during SMB Negotiate, retrying with NetBIOS session\n");
862+
863+
/*
864+
* Before reconnect, delete all pending mids for this
865+
* server, so reconnect would not signal connection
866+
* aborted error to mid's callbacks. Note that for this
867+
* server there should be exactly one pending mid
868+
* corresponding to SMB1/SMB2 Negotiate packet.
869+
*/
870+
spin_lock(&server->mid_lock);
871+
list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) {
872+
kref_get(&mid->refcount);
873+
list_move(&mid->qhead, &dispose_list);
874+
mid->mid_flags |= MID_DELETED;
875+
}
876+
spin_unlock(&server->mid_lock);
877+
878+
/* Now try to reconnect once with NetBIOS session. */
879+
server->with_rfc1001 = true;
880+
rc = cifs_reconnect_once(server);
881+
882+
/*
883+
* If reconnect was successful then indicate -EAGAIN
884+
* to mid's caller. If reconnect failed with -EAGAIN
885+
* then mask it as -EHOSTDOWN, so mid's caller would
886+
* know that it failed.
887+
*/
888+
if (rc == 0)
889+
mid_rc = -EAGAIN;
890+
else if (rc == -EAGAIN)
891+
mid_rc = -EHOSTDOWN;
892+
else
893+
mid_rc = rc;
894+
895+
/*
896+
* After reconnect (either successful or unsuccessful)
897+
* deliver reconnect status to mid's caller via mid's
898+
* callback. Use MID_RC state which indicates that the
899+
* return code should be read from mid_rc member.
900+
*/
901+
list_for_each_entry_safe(mid, nmid, &dispose_list, qhead) {
902+
list_del_init(&mid->qhead);
903+
mid->mid_rc = mid_rc;
904+
mid->mid_state = MID_RC;
905+
mid->callback(mid);
906+
release_mid(mid);
907+
}
908+
909+
/*
910+
* If reconnect failed then wait two seconds. In most
911+
* cases we were been called from the mount context and
912+
* delivered failure to mid's callback will stop this
913+
* receiver task thread and fails the mount process.
914+
* So wait two seconds to prevent another reconnect
915+
* in this task thread, which would be useless as the
916+
* mount context will fail at all.
917+
*/
918+
if (rc != 0)
919+
msleep(2000);
920+
} else {
921+
cifs_server_dbg(VFS, "RFC 1002 negative session response (unexpected)\n");
922+
cifs_reconnect(server, true);
923+
}
924+
break;
925+
case RFC1002_RETARGET_SESSION_RESPONSE:
926+
cifs_server_dbg(VFS, "RFC 1002 retarget session response (unexpected)\n");
826927
cifs_reconnect(server, true);
827928
break;
828929
default:
@@ -1702,6 +1803,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
17021803
memcpy(tcp_ses->server_RFC1001_name,
17031804
ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
17041805
tcp_ses->rfc1001_sessinit = ctx->rfc1001_sessinit;
1806+
tcp_ses->with_rfc1001 = false;
17051807
tcp_ses->session_estab = false;
17061808
tcp_ses->sequence_number = 0;
17071809
tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */
@@ -3218,6 +3320,7 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
32183320
return -EIO;
32193321
}
32203322

3323+
server->with_rfc1001 = true;
32213324
return 0;
32223325
}
32233326

@@ -3336,7 +3439,8 @@ generic_ip_connect(struct TCP_Server_Info *server)
33363439
* server port (139) and it was not explicitly disabled by mount option
33373440
* -o nonbsessinit.
33383441
*/
3339-
if (server->rfc1001_sessinit == 1 ||
3442+
if (server->with_rfc1001 ||
3443+
server->rfc1001_sessinit == 1 ||
33403444
(server->rfc1001_sessinit == -1 && sport == htons(RFC1001_PORT)))
33413445
rc = ip_rfc1001_connect(server);
33423446

fs/smb/client/transport.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,9 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
894894
case MID_SHUTDOWN:
895895
rc = -EHOSTDOWN;
896896
break;
897+
case MID_RC:
898+
rc = mid->mid_rc;
899+
break;
897900
default:
898901
if (!(mid->mid_flags & MID_DELETED)) {
899902
list_del_init(&mid->qhead);

0 commit comments

Comments
 (0)