summaryrefslogtreecommitdiff
path: root/fs/smb/client/connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smb/client/connect.c')
-rw-r--r--fs/smb/client/connect.c115
1 files changed, 112 insertions, 3 deletions
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index c808e2662857..a75db2dbd53f 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -28,6 +28,7 @@
#include <linux/uaccess.h>
#include <asm/processor.h>
#include <linux/inet.h>
+#include <net/inet_common.h>
#include <linux/module.h>
#include <keys/user-type.h>
#include <net/ipv6.h>
@@ -1548,6 +1549,17 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
server->session_key.len = 0;
kfree(server->hostname);
server->hostname = NULL;
+ if (server->iface_names[0]) {
+ int i;
+
+ for (i = 0; i <= server->__iface_idx; i++) {
+ if (server->iface_names[i]) {
+ kfree(server->iface_names[i]);
+ server->iface_names[i] = NULL;
+ }
+ }
+ server->__iface_idx = -1;
+ }
task = xchg(&server->tsk, NULL);
if (task)
@@ -1598,7 +1610,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
struct TCP_Server_Info *primary_server)
{
struct TCP_Server_Info *tcp_ses = NULL;
- int rc;
+ int rc, i;
cifs_dbg(FYI, "UNC: %s\n", ctx->UNC);
@@ -1677,6 +1689,24 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
sizeof(tcp_ses->srcaddr));
memcpy(&tcp_ses->dstaddr, &ctx->dstaddr,
sizeof(tcp_ses->dstaddr));
+
+ tcp_ses->__iface_idx = -1;
+
+ if (ctx->iface_names[0]) {
+ int max = primary_server ? 1 : CIFS_MAX_CHANNELS;
+ char *iface_name;
+
+ i = 0;
+ while (i < max && (iface_name = ctx->iface_names[i])) {
+ tcp_ses->iface_names[i] = kstrdup(iface_name, GFP_KERNEL);
+ if (unlikely(!tcp_ses->iface_names[i]))
+ goto out_err;
+ i++;
+ }
+
+ tcp_ses->__iface_idx = i ? i - 1 : -1;
+ }
+
if (ctx->use_client_guid)
memcpy(tcp_ses->client_guid, ctx->client_guid,
SMB2_CLIENT_GUID_SIZE);
@@ -1778,6 +1808,12 @@ out_err:
kfree(tcp_ses->leaf_fullpath);
if (tcp_ses->ssocket)
sock_release(tcp_ses->ssocket);
+ for (i = 0; i <= tcp_ses->__iface_idx; i++) {
+ if (tcp_ses->iface_names[i]) {
+ kfree(tcp_ses->iface_names[i]);
+ tcp_ses->iface_names[i] = NULL;
+ }
+ }
kfree(tcp_ses);
}
return ERR_PTR(rc);
@@ -2849,11 +2885,80 @@ static void rfc1002mangle(char *target, char *source, unsigned int length)
}
+static inline int __smb_get_sock_addr(struct socket *sock, struct sockaddr *sa, bool peer)
+{
+ int addrlen; /* return value from kernel_getsockname() */
+
+ if (peer)
+ addrlen = kernel_getpeername(sock, sa);
+ else
+ addrlen = kernel_getsockname(sock, sa);
+
+ if (addrlen != sizeof(struct sockaddr_in) && addrlen != sizeof(struct sockaddr_in6))
+ return addrlen < 0 ? addrlen : -EAFNOSUPPORT;
+
+ return 0;
+}
+
+static inline int smb_get_srcaddr(struct socket *sock, struct sockaddr *srcaddr)
+{
+ int rc;
+
+ rc = __smb_get_sock_addr(sock, srcaddr, false);
+ if (rc)
+ cifs_dbg(VFS, "%s: failed to get src address, rc=%d\n", __func__, rc);
+
+ return rc;
+}
+
+static inline int smb_get_dstaddr(struct socket *sock, struct sockaddr *dstaddr)
+{
+ int rc;
+
+ rc = __smb_get_sock_addr(sock, dstaddr, true);
+ if (rc)
+ cifs_dbg(VFS, "%s: failed to get dst address, rc=%d\n", __func__, rc);
+
+ return rc;
+}
+
+static inline int smb_get_sock_addrs(struct socket *sock, struct sockaddr *srcaddr,
+ struct sockaddr *dstaddr)
+{
+ int rc;
+
+ rc = smb_get_srcaddr(sock, srcaddr);
+ if (!rc)
+ rc = smb_get_dstaddr(sock, dstaddr);
+ if (rc)
+ return rc;
+
+ cifs_dbg(VFS, "%s: socket @ 0x%p srcaddr=%pISc, dstaddr=%pISc\n",
+ __func__, sock, srcaddr, dstaddr);
+
+ return 0;
+}
+
+static inline int bind_iface_name(struct socket *sock, const char *iface_name)
+{
+ sockptr_t optval = KERNEL_SOCKPTR((char *)iface_name);
+
+ return sock_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, optval, strlen(iface_name));
+}
+
static int
bind_socket(struct TCP_Server_Info *server)
{
+ char *iface_name = server->iface_names[0];
int rc = 0;
- if (server->srcaddr.ss_family != AF_UNSPEC) {
+
+ if (iface_name) {
+ rc = bind_iface_name(server->ssocket, iface_name);
+ if (rc) {
+ cifs_dbg(VFS, "failed to bind to interface %s, rc=%d\n", iface_name, rc);
+ return rc;
+ }
+ } else if (server->srcaddr.ss_family != AF_UNSPEC) {
/* Bind to the specified local IP address */
struct socket *socket = server->ssocket;
rc = socket->ops->bind(socket,
@@ -2872,6 +2977,7 @@ bind_socket(struct TCP_Server_Info *server)
&saddr4->sin_addr.s_addr, rc);
}
}
+
return rc;
}
@@ -2937,7 +3043,7 @@ generic_ip_connect(struct TCP_Server_Info *server)
__be16 sport;
int slen, sfamily;
struct socket *socket = server->ssocket;
- struct sockaddr *saddr;
+ struct sockaddr *saddr, _src, _dst;
saddr = (struct sockaddr *) &server->dstaddr;
@@ -3022,6 +3128,9 @@ generic_ip_connect(struct TCP_Server_Info *server)
server->ssocket = NULL;
return rc;
}
+
+ (void) smb_get_sock_addrs(socket, &_src, &_dst);
+
trace_smb3_connect_done(server->hostname, server->conn_id, &server->dstaddr);
if (sport == htons(RFC1001_PORT))
rc = ip_rfc1001_connect(server);