From patchwork Thu Aug 26 17:34:56 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Greear X-Patchwork-Id: 135261 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o7QHZTFK028399 for ; Thu, 26 Aug 2010 17:35:29 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752860Ab0HZRfR (ORCPT ); Thu, 26 Aug 2010 13:35:17 -0400 Received: from mail.candelatech.com ([208.74.158.172]:37994 "EHLO ns3.lanforge.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752857Ab0HZRfR (ORCPT ); Thu, 26 Aug 2010 13:35:17 -0400 Received: from localhost.localdomain (firewall.candelatech.com [70.89.124.249]) by ns3.lanforge.com (8.14.2/8.14.2) with ESMTP id o7QHYuNm011319 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 26 Aug 2010 10:34:57 -0700 From: Ben Greear To: sfrench@samba.org Cc: linux-cifs@vger.kernel.org, Ben Greear Subject: [cifs bindaddr v2] cifs: Allow binding to local IP address. Date: Thu, 26 Aug 2010 10:34:56 -0700 Message-Id: <1282844096-13295-1-git-send-email-greearb@candelatech.com> X-Mailer: git-send-email 1.6.2.5 Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Thu, 26 Aug 2010 17:35:29 +0000 (UTC) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index b7431af..77293db 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -374,6 +374,10 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) if (tcon->ses->domainName) seq_printf(s, ",domain=%s", tcon->ses->domainName); + if (tcon->ses->server->ip4_local_ip) + seq_printf(s, ",bindaddr=%pI4", + &tcon->ses->server->ip4_local_ip); + seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) seq_printf(s, ",forceuid"); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c9d0cfc..c0176d8 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -157,6 +157,7 @@ struct TCP_Server_Info { struct sockaddr_in sockAddr; struct sockaddr_in6 sockAddr6; } addr; + u32 ip4_local_ip; wait_queue_head_t response_q; wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ struct list_head pending_mid_q; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index ec0ea4a..bacbf46 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -105,6 +105,7 @@ struct smb_vol { bool sockopt_tcp_nodelay:1; unsigned short int port; char *prepath; + u32 local_ip; /* allow binding to a local IP address if != 0 */ struct nls_table *local_nls; }; @@ -1064,6 +1065,32 @@ cifs_parse_mount_options(char *options, const char *devname, "long\n"); return 1; } + } else if (strnicmp(data, "bindaddr", 8) == 0) { + struct sockaddr_storage laddr; + memset(&laddr, 0, sizeof(laddr)); + + if (!value || !*value) { + printk(KERN_WARNING "CIFS: bindaddr value" + " not specified.\n"); + return 1; /* needs_arg; */ + } + i = cifs_convert_address((struct sockaddr *)(&laddr), + value, strlen(value)); + if (i < 0) { + vol->local_ip = 0; + printk(KERN_WARNING "CIFS: Could not parse" + " bindaddr: %s\n", + value); + return 1; + } else { + struct sockaddr_in *s4; + s4 = (struct sockaddr_in *)&laddr; + if (s4->sin_family == AF_INET) + vol->local_ip = s4->sin_addr.s_addr; + else + printk(KERN_WARNING "WARNING: IPv6 " + "bindaddr not supported yet.\n"); + } } else if (strnicmp(data, "prefixpath", 10) == 0) { if (!value || !*value) { printk(KERN_WARNING @@ -1393,7 +1420,8 @@ cifs_parse_mount_options(char *options, const char *devname, } static bool -match_address(struct TCP_Server_Info *server, struct sockaddr *addr) +match_address(struct TCP_Server_Info *server, struct sockaddr *addr, + u32 local_ip4) { struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; @@ -1406,6 +1434,8 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr) if (addr4->sin_port && addr4->sin_port != server->addr.sockAddr.sin_port) return false; + if (local_ip4 && (local_ip4 != server->ip4_local_ip)) + return false; break; case AF_INET6: if (!ipv6_addr_equal(&addr6->sin6_addr, @@ -1487,7 +1517,7 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) if (server->tcpStatus == CifsNew) continue; - if (!match_address(server, addr)) + if (!match_address(server, addr, vol->local_ip)) continue; if (!match_security(server, vol)) @@ -1602,6 +1632,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) * no need to spinlock this init of tcpStatus or srv_count */ tcp_ses->tcpStatus = CifsNew; + tcp_ses->ip4_local_ip = volume_info->local_ip; ++tcp_ses->srv_count; if (addr.ss_family == AF_INET6) { @@ -1678,6 +1709,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) vol->password ? vol->password : "", MAX_PASSWORD_SIZE)) continue; + if (server->ip4_local_ip && + (server->ip4_local_ip != ses->server->ip4_local_ip)) + continue; + /* TODO: Deal with IPv6 local addr matching? --Ben */ } ++ses->ses_count; write_unlock(&cifs_tcp_ses_lock); @@ -2051,6 +2086,20 @@ ipv4_connect(struct TCP_Server_Info *server) cifs_reclassify_socket4(socket); } + /* Bind to the local IP address if specified */ + if (server->ip4_local_ip) { + struct sockaddr_in myaddr = { + .sin_family = AF_INET, + }; + myaddr.sin_addr.s_addr = server->ip4_local_ip; + myaddr.sin_port = 0; /* any */ + rc = socket->ops->bind(socket, (struct sockaddr *) &myaddr, + sizeof(myaddr)); + if (rc < 0) + cERROR(1, "Failed to bind to: %pI4, error: %d\n", + &server->ip4_local_ip, rc); + } + /* user overrode default port */ if (server->addr.sockAddr.sin_port) { rc = socket->ops->connect(socket, (struct sockaddr *)