diff options
author | Ben Greear <greearb@candelatech.com> | 2010-09-01 20:06:02 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2010-09-29 15:04:29 -0400 |
commit | 3eb9a8893a76cf1cda3b41c3212eb2cfe83eae0e (patch) | |
tree | fd4fb596113f27bcbe3bba1f2777c91f14521f48 | |
parent | 2b149f11978b44199954710d32c0eecf6c9efd9c (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.c | 19 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 1 | ||||
-rw-r--r-- | fs/cifs/connect.c | 90 |
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 | */ | ||
1398 | static bool | ||
1399 | srcip_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 | |||
1377 | static bool | 1421 | static bool |
1378 | match_address(struct TCP_Server_Info *server, struct sockaddr *addr) | 1422 | match_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 | ||
2053 | static int | ||
2054 | bind_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 | ||
2003 | static int | 2081 | static int |
2004 | ipv4_connect(struct TCP_Server_Info *server) | 2082 | ipv4_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, |