Skip to content

Commit d70e9fa

Browse files
aaptelsmfrench
authored andcommitted
cifs: try opening channels after mounting
After doing mount() successfully we call cifs_try_adding_channels() which will open as many channels as it can. Channels are closed when the master session is closed. The master connection becomes the first channel. ,-------------> global cifs_tcp_ses_list <-------------------------. | | '- TCP_Server_Info <--> TCP_Server_Info <--> TCP_Server_Info <-' (master con) (chan#1 con) (chan#2 con) | ^ ^ ^ v '--------------------|--------------------' cifs_ses | - chan_count = 3 | - chans[] ---------------------' - smb3signingkey[] (master signing key) Note how channel connections don't have sessions. That's because cifs_ses can only be part of one linked list (list_head are internal to the elements). For signing keys, each channel has its own signing key which must be used only after the channel has been bound. While it's binding it must use the master session signing key. For encryption keys, since channel connections do not have sessions attached we must now find matching session by looping over all sessions in smb2_get_enc_key(). Each channel is opened like a regular server connection but at the session setup request step it must set the SMB2_SESSION_REQ_FLAG_BINDING flag and use the session id to bind to. Finally, while sending in compound_send_recv() for requests that aren't negprot, ses-setup or binding related, use a channel by cycling through the available ones (round-robin). Signed-off-by: Aurelien Aptel <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent b8f7442 commit d70e9fa

File tree

9 files changed

+478
-88
lines changed

9 files changed

+478
-88
lines changed

fs/cifs/cifsglob.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,8 @@ struct cifs_ses {
10011001
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
10021002
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
10031003

1004+
__u8 binding_preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
1005+
10041006
/*
10051007
* Network interfaces available on the server this session is
10061008
* connected to.
@@ -1022,6 +1024,20 @@ struct cifs_ses {
10221024
atomic_t chan_seq; /* round robin state */
10231025
};
10241026

1027+
/*
1028+
* When binding a new channel, we need to access the channel which isn't fully
1029+
* established yet (one past the established count)
1030+
*/
1031+
1032+
static inline
1033+
struct cifs_chan *cifs_ses_binding_channel(struct cifs_ses *ses)
1034+
{
1035+
if (ses->binding)
1036+
return &ses->chans[ses->chan_count];
1037+
else
1038+
return NULL;
1039+
}
1040+
10251041
static inline
10261042
struct TCP_Server_Info *cifs_ses_server(struct cifs_ses *ses)
10271043
{

fs/cifs/cifsproto.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
243243
struct tcon_link *tlink,
244244
struct cifs_pending_open *open);
245245
extern void cifs_del_pending_open(struct cifs_pending_open *open);
246+
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb_vol *vol);
246247
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
247248
int from_reconnect);
248249
extern void cifs_put_tcon(struct cifs_tcon *tcon);
@@ -585,6 +586,12 @@ void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
585586

586587
extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
587588
unsigned int *len, unsigned int *offset);
589+
int cifs_try_adding_channels(struct cifs_ses *ses);
590+
int cifs_ses_add_channel(struct cifs_ses *ses,
591+
struct cifs_server_iface *iface);
592+
bool is_server_using_iface(struct TCP_Server_Info *server,
593+
struct cifs_server_iface *iface);
594+
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
588595

589596
void extract_unc_hostname(const char *unc, const char **h, size_t *len);
590597
int copy_path_name(char *dst, const char *src);

fs/cifs/connect.c

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,7 +2745,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
27452745
send_sig(SIGKILL, task, 1);
27462746
}
27472747

2748-
static struct TCP_Server_Info *
2748+
struct TCP_Server_Info *
27492749
cifs_get_tcp_session(struct smb_vol *volume_info)
27502750
{
27512751
struct TCP_Server_Info *tcp_ses = NULL;
@@ -3074,6 +3074,14 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
30743074
list_del_init(&ses->smb_ses_list);
30753075
spin_unlock(&cifs_tcp_ses_lock);
30763076

3077+
/* close any extra channels */
3078+
if (ses->chan_count > 1) {
3079+
int i;
3080+
3081+
for (i = 1; i < ses->chan_count; i++)
3082+
cifs_put_tcp_session(ses->chans[i].server, 0);
3083+
}
3084+
30773085
sesInfoFree(ses);
30783086
cifs_put_tcp_session(server, 0);
30793087
}
@@ -3320,14 +3328,25 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
33203328
ses->sectype = volume_info->sectype;
33213329
ses->sign = volume_info->sign;
33223330
mutex_lock(&ses->session_mutex);
3331+
3332+
/* add server as first channel */
3333+
ses->chans[0].server = server;
3334+
ses->chan_count = 1;
3335+
ses->chan_max = volume_info->multichannel ? volume_info->max_channels:1;
3336+
33233337
rc = cifs_negotiate_protocol(xid, ses);
33243338
if (!rc)
33253339
rc = cifs_setup_session(xid, ses, volume_info->local_nls);
3340+
3341+
/* each channel uses a different signing key */
3342+
memcpy(ses->chans[0].signkey, ses->smb3signingkey,
3343+
sizeof(ses->smb3signingkey));
3344+
33263345
mutex_unlock(&ses->session_mutex);
33273346
if (rc)
33283347
goto get_ses_fail;
33293348

3330-
/* success, put it on the list */
3349+
/* success, put it on the list and add it as first channel */
33313350
spin_lock(&cifs_tcp_ses_lock);
33323351
list_add(&ses->smb_ses_list, &server->smb_ses_list);
33333352
spin_unlock(&cifs_tcp_ses_lock);
@@ -4940,6 +4959,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
49404959
cifs_autodisable_serverino(cifs_sb);
49414960
out:
49424961
free_xid(xid);
4962+
cifs_try_adding_channels(ses);
49434963
return mount_setup_tlink(cifs_sb, ses, tcon);
49444964

49454965
error:
@@ -5214,21 +5234,23 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
52145234
int rc = -ENOSYS;
52155235
struct TCP_Server_Info *server = cifs_ses_server(ses);
52165236

5217-
ses->capabilities = server->capabilities;
5218-
if (linuxExtEnabled == 0)
5219-
ses->capabilities &= (~server->vals->cap_unix);
5237+
if (!ses->binding) {
5238+
ses->capabilities = server->capabilities;
5239+
if (linuxExtEnabled == 0)
5240+
ses->capabilities &= (~server->vals->cap_unix);
5241+
5242+
if (ses->auth_key.response) {
5243+
cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
5244+
ses->auth_key.response);
5245+
kfree(ses->auth_key.response);
5246+
ses->auth_key.response = NULL;
5247+
ses->auth_key.len = 0;
5248+
}
5249+
}
52205250

52215251
cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
52225252
server->sec_mode, server->capabilities, server->timeAdj);
52235253

5224-
if (ses->auth_key.response) {
5225-
cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
5226-
ses->auth_key.response);
5227-
kfree(ses->auth_key.response);
5228-
ses->auth_key.response = NULL;
5229-
ses->auth_key.len = 0;
5230-
}
5231-
52325254
if (server->ops->sess_setup)
52335255
rc = server->ops->sess_setup(xid, ses, nls_info);
52345256

fs/cifs/sess.c

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,219 @@
3131
#include <linux/utsname.h>
3232
#include <linux/slab.h>
3333
#include "cifs_spnego.h"
34+
#include "smb2proto.h"
35+
36+
bool
37+
is_server_using_iface(struct TCP_Server_Info *server,
38+
struct cifs_server_iface *iface)
39+
{
40+
struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr;
41+
struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr;
42+
struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr;
43+
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr;
44+
45+
if (server->dstaddr.ss_family != iface->sockaddr.ss_family)
46+
return false;
47+
if (server->dstaddr.ss_family == AF_INET) {
48+
if (s4->sin_addr.s_addr != i4->sin_addr.s_addr)
49+
return false;
50+
} else if (server->dstaddr.ss_family == AF_INET6) {
51+
if (memcmp(&s6->sin6_addr, &i6->sin6_addr,
52+
sizeof(i6->sin6_addr)) != 0)
53+
return false;
54+
} else {
55+
/* unknown family.. */
56+
return false;
57+
}
58+
return true;
59+
}
60+
61+
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
62+
{
63+
int i;
64+
65+
for (i = 0; i < ses->chan_count; i++) {
66+
if (is_server_using_iface(ses->chans[i].server, iface))
67+
return true;
68+
}
69+
return false;
70+
}
71+
72+
/* returns number of channels added */
73+
int cifs_try_adding_channels(struct cifs_ses *ses)
74+
{
75+
int old_chan_count = ses->chan_count;
76+
int left = ses->chan_max - ses->chan_count;
77+
int i = 0;
78+
int rc = 0;
79+
80+
if (left <= 0) {
81+
cifs_dbg(FYI,
82+
"ses already at max_channels (%zu), nothing to open\n",
83+
ses->chan_max);
84+
return 0;
85+
}
86+
87+
if (ses->server->dialect < SMB30_PROT_ID) {
88+
cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
89+
return 0;
90+
}
91+
92+
/* ifaces are sorted by speed, try them in order */
93+
for (i = 0; left > 0 && i < ses->iface_count; i++) {
94+
struct cifs_server_iface *iface;
95+
96+
iface = &ses->iface_list[i];
97+
if (is_ses_using_iface(ses, iface) && !iface->rss_capable)
98+
continue;
99+
100+
rc = cifs_ses_add_channel(ses, iface);
101+
if (rc) {
102+
cifs_dbg(FYI, "failed to open extra channel\n");
103+
continue;
104+
}
105+
106+
cifs_dbg(FYI, "successfully opened new channel\n");
107+
left--;
108+
}
109+
110+
/*
111+
* TODO: if we still have channels left to open try to connect
112+
* to same RSS-capable iface multiple times
113+
*/
114+
115+
return ses->chan_count - old_chan_count;
116+
}
117+
118+
int
119+
cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface)
120+
{
121+
struct cifs_chan *chan;
122+
struct smb_vol vol = {NULL};
123+
static const char unc_fmt[] = "\\%s\\foo";
124+
char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0};
125+
struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
126+
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
127+
int rc;
128+
unsigned int xid = get_xid();
129+
130+
cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ",
131+
ses, iface->speed, iface->rdma_capable ? "yes" : "no");
132+
if (iface->sockaddr.ss_family == AF_INET)
133+
cifs_dbg(FYI, "ip:%pI4)\n", &ipv4->sin_addr);
134+
else
135+
cifs_dbg(FYI, "ip:%pI6)\n", &ipv6->sin6_addr);
136+
137+
/*
138+
* Setup a smb_vol with mostly the same info as the existing
139+
* session and overwrite it with the requested iface data.
140+
*
141+
* We need to setup at least the fields used for negprot and
142+
* sesssetup.
143+
*
144+
* We only need the volume here, so we can reuse memory from
145+
* the session and server without caring about memory
146+
* management.
147+
*/
148+
149+
/* Always make new connection for now (TODO?) */
150+
vol.nosharesock = true;
151+
152+
/* Auth */
153+
vol.domainauto = ses->domainAuto;
154+
vol.domainname = ses->domainName;
155+
vol.username = ses->user_name;
156+
vol.password = ses->password;
157+
vol.sectype = ses->sectype;
158+
vol.sign = ses->sign;
159+
160+
/* UNC and paths */
161+
/* XXX: Use ses->server->hostname? */
162+
sprintf(unc, unc_fmt, ses->serverName);
163+
vol.UNC = unc;
164+
vol.prepath = "";
165+
166+
/* Re-use same version as master connection */
167+
vol.vals = ses->server->vals;
168+
vol.ops = ses->server->ops;
169+
170+
vol.noblocksnd = ses->server->noblocksnd;
171+
vol.noautotune = ses->server->noautotune;
172+
vol.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
173+
vol.echo_interval = ses->server->echo_interval / HZ;
174+
175+
/*
176+
* This will be used for encoding/decoding user/domain/pw
177+
* during sess setup auth.
178+
*
179+
* XXX: We use the default for simplicity but the proper way
180+
* would be to use the one that ses used, which is not
181+
* stored. This might break when dealing with non-ascii
182+
* strings.
183+
*/
184+
vol.local_nls = load_nls_default();
185+
186+
/* Use RDMA if possible */
187+
vol.rdma = iface->rdma_capable;
188+
memcpy(&vol.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage));
189+
190+
/* reuse master con client guid */
191+
memcpy(&vol.client_guid, ses->server->client_guid,
192+
SMB2_CLIENT_GUID_SIZE);
193+
vol.use_client_guid = true;
194+
195+
mutex_lock(&ses->session_mutex);
196+
197+
chan = &ses->chans[ses->chan_count];
198+
chan->server = cifs_get_tcp_session(&vol);
199+
if (IS_ERR(chan->server)) {
200+
rc = PTR_ERR(chan->server);
201+
chan->server = NULL;
202+
goto out;
203+
}
204+
205+
/*
206+
* We need to allocate the server crypto now as we will need
207+
* to sign packets before we generate the channel signing key
208+
* (we sign with the session key)
209+
*/
210+
rc = smb311_crypto_shash_allocate(chan->server);
211+
if (rc) {
212+
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
213+
goto out;
214+
}
215+
216+
ses->binding = true;
217+
rc = cifs_negotiate_protocol(xid, ses);
218+
if (rc)
219+
goto out;
220+
221+
rc = cifs_setup_session(xid, ses, vol.local_nls);
222+
if (rc)
223+
goto out;
224+
225+
/* success, put it on the list
226+
* XXX: sharing ses between 2 tcp server is not possible, the
227+
* way "internal" linked lists works in linux makes element
228+
* only able to belong to one list
229+
*
230+
* the binding session is already established so the rest of
231+
* the code should be able to look it up, no need to add the
232+
* ses to the new server.
233+
*/
234+
235+
ses->chan_count++;
236+
atomic_set(&ses->chan_seq, 0);
237+
out:
238+
ses->binding = false;
239+
mutex_unlock(&ses->session_mutex);
240+
241+
if (rc && chan->server)
242+
cifs_put_tcp_session(chan->server, 0);
243+
unload_nls(vol.local_nls);
244+
245+
return rc;
246+
}
34247

35248
static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
36249
{

0 commit comments

Comments
 (0)