Skip to content

Commit 3eb9a88

Browse files
greearbSteve French
authored andcommitted
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 <[email protected]> Acked-by: Dr. David Holder <[email protected]> Signed-off-by: Ben Greear <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 2b149f1 commit 3eb9a88

File tree

3 files changed

+108
-2
lines changed

3 files changed

+108
-2
lines changed

fs/cifs/cifsfs.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <linux/kthread.h>
3737
#include <linux/freezer.h>
3838
#include <linux/smp_lock.h>
39+
#include <net/ipv6.h>
3940
#include "cifsfs.h"
4041
#include "cifspdu.h"
4142
#define DECLARE_GLOBALS_HERE
@@ -367,13 +368,31 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)
367368
{
368369
struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb);
369370
struct cifsTconInfo *tcon = cifs_sb->tcon;
371+
struct sockaddr *srcaddr;
372+
srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr;
370373

371374
seq_printf(s, ",unc=%s", tcon->treeName);
372375
if (tcon->ses->userName)
373376
seq_printf(s, ",username=%s", tcon->ses->userName);
374377
if (tcon->ses->domainName)
375378
seq_printf(s, ",domain=%s", tcon->ses->domainName);
376379

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+
377396
seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
378397
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
379398
seq_printf(s, ",forceuid");

fs/cifs/cifsglob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ struct TCP_Server_Info {
139139
struct sockaddr_in sockAddr;
140140
struct sockaddr_in6 sockAddr6;
141141
} addr;
142+
struct sockaddr_storage srcaddr; /* locally bind to this IP */
142143
wait_queue_head_t response_q;
143144
wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
144145
struct list_head pending_mid_q;

fs/cifs/connect.c

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ struct smb_vol {
105105
bool sockopt_tcp_nodelay:1;
106106
unsigned short int port;
107107
char *prepath;
108+
struct sockaddr_storage srcaddr; /* allow binding to a local IP */
108109
struct nls_table *local_nls;
109110
};
110111

@@ -1046,6 +1047,22 @@ cifs_parse_mount_options(char *options, const char *devname,
10461047
"long\n");
10471048
return 1;
10481049
}
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+
}
10491066
} else if (strnicmp(data, "prefixpath", 10) == 0) {
10501067
if (!value || !*value) {
10511068
printk(KERN_WARNING
@@ -1374,8 +1391,36 @@ cifs_parse_mount_options(char *options, const char *devname,
13741391
return 0;
13751392
}
13761393

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+
13771421
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)
13791424
{
13801425
struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
13811426
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
@@ -1402,6 +1447,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr)
14021447
break;
14031448
}
14041449

1450+
if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr))
1451+
return false;
1452+
14051453
return true;
14061454
}
14071455

@@ -1469,7 +1517,8 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol)
14691517
if (server->tcpStatus == CifsNew)
14701518
continue;
14711519

1472-
if (!match_address(server, addr))
1520+
if (!match_address(server, addr,
1521+
(struct sockaddr *)&vol->srcaddr))
14731522
continue;
14741523

14751524
if (!match_security(server, vol))
@@ -1584,6 +1633,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
15841633
* no need to spinlock this init of tcpStatus or srv_count
15851634
*/
15861635
tcp_ses->tcpStatus = CifsNew;
1636+
memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
1637+
sizeof(tcp_ses->srcaddr));
15871638
++tcp_ses->srv_count;
15881639

15891640
if (addr.ss_family == AF_INET6) {
@@ -1999,6 +2050,33 @@ static void rfc1002mangle(char *target, char *source, unsigned int length)
19992050

20002051
}
20012052

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+
}
20022080

20032081
static int
20042082
ipv4_connect(struct TCP_Server_Info *server)
@@ -2024,6 +2102,10 @@ ipv4_connect(struct TCP_Server_Info *server)
20242102
cifs_reclassify_socket4(socket);
20252103
}
20262104

2105+
rc = bind_socket(server);
2106+
if (rc < 0)
2107+
return rc;
2108+
20272109
/* user overrode default port */
20282110
if (server->addr.sockAddr.sin_port) {
20292111
rc = socket->ops->connect(socket, (struct sockaddr *)
@@ -2186,6 +2268,10 @@ ipv6_connect(struct TCP_Server_Info *server)
21862268
cifs_reclassify_socket6(socket);
21872269
}
21882270

2271+
rc = bind_socket(server);
2272+
if (rc < 0)
2273+
return rc;
2274+
21892275
/* user overrode default port */
21902276
if (server->addr.sockAddr6.sin6_port) {
21912277
rc = socket->ops->connect(socket,

0 commit comments

Comments
 (0)