diff options
Diffstat (limited to 'fs/smb/client/connect.c')
-rw-r--r-- | fs/smb/client/connect.c | 115 |
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); |