aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Greear <greearb@candelatech.com>2010-09-01 20:06:02 -0400
committerSteve French <sfrench@us.ibm.com>2010-09-29 15:04:29 -0400
commit3eb9a8893a76cf1cda3b41c3212eb2cfe83eae0e (patch)
treefd4fb596113f27bcbe3bba1f2777c91f14521f48
parent2b149f11978b44199954710d32c0eecf6c9efd9c (diff)
cifs: Allow binding to local IP address.
When using multi-homed machines, it's nice to be able to specify the local IP to use for outbound connections. This patch gives cifs the ability to bind to a particular IP address. Usage: mount -t cifs -o srcaddr=192.168.1.50,user=foo, ... Usage: mount -t cifs -o srcaddr=2002::100:1,user=foo, ... Acked-by: Jeff Layton <jlayton@redhat.com> Acked-by: Dr. David Holder <david.holder@erion.co.uk> Signed-off-by: Ben Greear <greearb@candelatech.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r--fs/cifs/cifsfs.c19
-rw-r--r--fs/cifs/cifsglob.h1
-rw-r--r--fs/cifs/connect.c90
3 files changed, 108 insertions, 2 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index b7431afdd76d..1b6ddd6f760f 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -36,6 +36,7 @@
36#include <linux/kthread.h> 36#include <linux/kthread.h>
37#include <linux/freezer.h> 37#include <linux/freezer.h>
38#include <linux/smp_lock.h> 38#include <linux/smp_lock.h>
39#include <net/ipv6.h>
39#include "cifsfs.h" 40#include "cifsfs.h"
40#include "cifspdu.h" 41#include "cifspdu.h"
41#define DECLARE_GLOBALS_HERE 42#define DECLARE_GLOBALS_HERE
@@ -367,6 +368,8 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)
367{ 368{
368 struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb); 369 struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb);
369 struct cifsTconInfo *tcon = cifs_sb->tcon; 370 struct cifsTconInfo *tcon = cifs_sb->tcon;
371 struct sockaddr *srcaddr;
372 srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr;
370 373
371 seq_printf(s, ",unc=%s", tcon->treeName); 374 seq_printf(s, ",unc=%s", tcon->treeName);
372 if (tcon->ses->userName) 375 if (tcon->ses->userName)
@@ -374,6 +377,22 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)
374 if (tcon->ses->domainName) 377 if (tcon->ses->domainName)
375 seq_printf(s, ",domain=%s", tcon->ses->domainName); 378 seq_printf(s, ",domain=%s", tcon->ses->domainName);
376 379
380 if (srcaddr->sa_family != AF_UNSPEC) {
381 struct sockaddr_in *saddr4;
382 struct sockaddr_in6 *saddr6;
383 saddr4 = (struct sockaddr_in *)srcaddr;
384 saddr6 = (struct sockaddr_in6 *)srcaddr;
385 if (srcaddr->sa_family == AF_INET6)
386 seq_printf(s, ",srcaddr=%pI6c",
387 &saddr6->sin6_addr);
388 else if (srcaddr->sa_family == AF_INET)
389 seq_printf(s, ",srcaddr=%pI4",
390 &saddr4->sin_addr.s_addr);
391 else
392 seq_printf(s, ",srcaddr=BAD-AF:%i",
393 (int)(srcaddr->sa_family));
394 }
395
377 seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); 396 seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
378 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) 397 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
379 seq_printf(s, ",forceuid"); 398 seq_printf(s, ",forceuid");
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index c68f31cf4550..6ef0efaf68d4 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -139,6 +139,7 @@ struct TCP_Server_Info {
139 struct sockaddr_in sockAddr; 139 struct sockaddr_in sockAddr;
140 struct sockaddr_in6 sockAddr6; 140 struct sockaddr_in6 sockAddr6;
141 } addr; 141 } addr;
142 struct sockaddr_storage srcaddr; /* locally bind to this IP */
142 wait_queue_head_t response_q; 143 wait_queue_head_t response_q;
143 wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ 144 wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
144 struct list_head pending_mid_q; 145 struct list_head pending_mid_q;
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index c99760a523bf..fa884520fb84 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -105,6 +105,7 @@ struct smb_vol {
105 bool sockopt_tcp_nodelay:1; 105 bool sockopt_tcp_nodelay:1;
106 unsigned short int port; 106 unsigned short int port;
107 char *prepath; 107 char *prepath;
108 struct sockaddr_storage srcaddr; /* allow binding to a local IP */
108 struct nls_table *local_nls; 109 struct nls_table *local_nls;
109}; 110};
110 111
@@ -1046,6 +1047,22 @@ cifs_parse_mount_options(char *options, const char *devname,
1046 "long\n"); 1047 "long\n");
1047 return 1; 1048 return 1;
1048 } 1049 }
1050 } else if (strnicmp(data, "srcaddr", 7) == 0) {
1051 vol->srcaddr.ss_family = AF_UNSPEC;
1052
1053 if (!value || !*value) {
1054 printk(KERN_WARNING "CIFS: srcaddr value"
1055 " not specified.\n");
1056 return 1; /* needs_arg; */
1057 }
1058 i = cifs_convert_address((struct sockaddr *)&vol->srcaddr,
1059 value, strlen(value));
1060 if (i < 0) {
1061 printk(KERN_WARNING "CIFS: Could not parse"
1062 " srcaddr: %s\n",
1063 value);
1064 return 1;
1065 }
1049 } else if (strnicmp(data, "prefixpath", 10) == 0) { 1066 } else if (strnicmp(data, "prefixpath", 10) == 0) {
1050 if (!value || !*value) { 1067 if (!value || !*value) {
1051 printk(KERN_WARNING 1068 printk(KERN_WARNING
@@ -1374,8 +1391,36 @@ cifs_parse_mount_options(char *options, const char *devname,
1374 return 0; 1391 return 0;
1375} 1392}
1376 1393
1394/** Returns true if srcaddr isn't specified and rhs isn't
1395 * specified, or if srcaddr is specified and
1396 * matches the IP address of the rhs argument.
1397 */
1398static bool
1399srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs)
1400{
1401 switch (srcaddr->sa_family) {
1402 case AF_UNSPEC:
1403 return (rhs->sa_family == AF_UNSPEC);
1404 case AF_INET: {
1405 struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
1406 struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
1407 return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr);
1408 }
1409 case AF_INET6: {
1410 struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
1411 struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)&rhs;
1412 return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr);
1413 }
1414 default:
1415 WARN_ON(1);
1416 return false; /* don't expect to be here */
1417 }
1418}
1419
1420
1377static bool 1421static bool
1378match_address(struct TCP_Server_Info *server, struct sockaddr *addr) 1422match_address(struct TCP_Server_Info *server, struct sockaddr *addr,
1423 struct sockaddr *srcaddr)
1379{ 1424{
1380 struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; 1425 struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
1381 struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; 1426 struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
@@ -1402,6 +1447,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr)
1402 break; 1447 break;
1403 } 1448 }
1404 1449
1450 if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr))
1451 return false;
1452
1405 return true; 1453 return true;
1406} 1454}
1407 1455
@@ -1469,7 +1517,8 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol)
1469 if (server->tcpStatus == CifsNew) 1517 if (server->tcpStatus == CifsNew)
1470 continue; 1518 continue;
1471 1519
1472 if (!match_address(server, addr)) 1520 if (!match_address(server, addr,
1521 (struct sockaddr *)&vol->srcaddr))
1473 continue; 1522 continue;
1474 1523
1475 if (!match_security(server, vol)) 1524 if (!match_security(server, vol))
@@ -1584,6 +1633,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
1584 * no need to spinlock this init of tcpStatus or srv_count 1633 * no need to spinlock this init of tcpStatus or srv_count
1585 */ 1634 */
1586 tcp_ses->tcpStatus = CifsNew; 1635 tcp_ses->tcpStatus = CifsNew;
1636 memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
1637 sizeof(tcp_ses->srcaddr));
1587 ++tcp_ses->srv_count; 1638 ++tcp_ses->srv_count;
1588 1639
1589 if (addr.ss_family == AF_INET6) { 1640 if (addr.ss_family == AF_INET6) {
@@ -1999,6 +2050,33 @@ static void rfc1002mangle(char *target, char *source, unsigned int length)
1999 2050
2000} 2051}
2001 2052
2053static int
2054bind_socket(struct TCP_Server_Info *server)
2055{
2056 int rc = 0;
2057 if (server->srcaddr.ss_family != AF_UNSPEC) {
2058 /* Bind to the specified local IP address */
2059 struct socket *socket = server->ssocket;
2060 rc = socket->ops->bind(socket,
2061 (struct sockaddr *) &server->srcaddr,
2062 sizeof(server->srcaddr));
2063 if (rc < 0) {
2064 struct sockaddr_in *saddr4;
2065 struct sockaddr_in6 *saddr6;
2066 saddr4 = (struct sockaddr_in *)&server->srcaddr;
2067 saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
2068 if (saddr6->sin6_family == AF_INET6)
2069 cERROR(1, "cifs: "
2070 "Failed to bind to: %pI6c, error: %d\n",
2071 &saddr6->sin6_addr, rc);
2072 else
2073 cERROR(1, "cifs: "
2074 "Failed to bind to: %pI4, error: %d\n",
2075 &saddr4->sin_addr.s_addr, rc);
2076 }
2077 }
2078 return rc;
2079}
2002 2080
2003static int 2081static int
2004ipv4_connect(struct TCP_Server_Info *server) 2082ipv4_connect(struct TCP_Server_Info *server)
@@ -2024,6 +2102,10 @@ ipv4_connect(struct TCP_Server_Info *server)
2024 cifs_reclassify_socket4(socket); 2102 cifs_reclassify_socket4(socket);
2025 } 2103 }
2026 2104
2105 rc = bind_socket(server);
2106 if (rc < 0)
2107 return rc;
2108
2027 /* user overrode default port */ 2109 /* user overrode default port */
2028 if (server->addr.sockAddr.sin_port) { 2110 if (server->addr.sockAddr.sin_port) {
2029 rc = socket->ops->connect(socket, (struct sockaddr *) 2111 rc = socket->ops->connect(socket, (struct sockaddr *)
@@ -2186,6 +2268,10 @@ ipv6_connect(struct TCP_Server_Info *server)
2186 cifs_reclassify_socket6(socket); 2268 cifs_reclassify_socket6(socket);
2187 } 2269 }
2188 2270
2271 rc = bind_socket(server);
2272 if (rc < 0)
2273 return rc;
2274
2189 /* user overrode default port */ 2275 /* user overrode default port */
2190 if (server->addr.sockAddr6.sin6_port) { 2276 if (server->addr.sockAddr6.sin6_port) {
2191 rc = socket->ops->connect(socket, 2277 rc = socket->ops->connect(socket,