Re: [cifs] cifs: Allow binding to local IP address.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 31 Aug 2010 09:44:39 -0700
Ben Greear <greearb@xxxxxxxxxxxxxxx> wrote:

> On 08/31/2010 07:06 AM, Jeff Layton wrote:
> > On Mon, 30 Aug 2010 22:40:22 -0700
> > Ben Greear<greearb@xxxxxxxxxxxxxxx>  wrote:
> >
> >> 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, ...
> 
> >> +bool
> >> +cifs_addr_is_specified(struct sockaddr *srcaddr) {
> >> +	struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
> >> +	struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
> >> +	switch (srcaddr->sa_family) {
> >> +	case AF_INET:
> >> +		return saddr4->sin_addr.s_addr != 0;
> > 						^^^^
> > 					nit: should probably be htonl(INADDR_ANY)
> 
> We don't initialize it to INADDR_ANY, so I'd rather not check
> against it, even though now and forever it's going to be zero.
> But, will change if that's the consensus.
> 

What may be best instead is to just set sa_family to AF_UNSPEC when
it's not specified. An sa_family that was AF_INET or AF_INET6 would
imply that it had been specified. That way someone could reasonably
force a ->bind to occur to INADDR_ANY (as silly as that sounds).

> >> @@ -1064,6 +1066,22 @@ cifs_parse_mount_options(char *options, const char *devname,
> >>   						    "long\n");
> >>   				return 1;
> >>   			}
> >> +		} else if (strnicmp(data, "srcaddr", 8) == 0) {
> >> +			memset(&vol->srcaddr, 0, sizeof(vol->srcaddr));
> > 			^^^^^^^^^
> > 			Is this necessary? vol is kzalloc'ed so it
> > 			should be zeroed out already. Or are you
> > 			worried about people specifying srcaddr= more
> > 			than once?
> 
> It just seems cleaner in general to set it to known value in case errors
> later in that method keep us from setting it to a proper new value.
> And, certainly useful if it can be set twice somehow..but not sure if that's
> currently the case or not.
> 

Fair enough. There's probably no need to zero this out entirely though.
Setting the family to AF_UNSPEC should be sufficient.

> >
> >> +
> >> +			if (!value || !*value) {
> >> +				printk(KERN_WARNING "CIFS: srcaddr value"
> >> +				       " not specified.\n");
> >> +				return 1;	/* needs_arg; */
> >> +			}
> >> +			i = cifs_convert_address((struct sockaddr *)&vol->srcaddr,
> >> +						 value, strlen(value));
> >> +			if (i<  0) {
> >> +				printk(KERN_WARNING "CIFS:  Could not parse"
> >> +				       " srcaddr: %s\n",
> >> +				       value);
> >> +				return 1;
> >> +			}
> >>   		} else if (strnicmp(data, "prefixpath", 10) == 0) {
> >>   			if (!value || !*value) {
> >>   				printk(KERN_WARNING
> >> @@ -1392,8 +1410,37 @@ cifs_parse_mount_options(char *options, const char *devname,
> >>   	return 0;
> >>   }
> >>
> >> +/** Returns true if srcaddr isn't specified or if it matches
> >> + * the IP address of the rhs argument.
> >> + */
> >>   static bool
> >> -match_address(struct TCP_Server_Info *server, struct sockaddr *addr)
> >> +srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs)
> >> +{
> >> +	if (cifs_addr_is_specified(srcaddr)) {
> >
> > 	Just wondering what the expected behavior would be here...
> >
> > 	Suppose I have a clustered IP address set up, and I want to
> > 	ensure that one of my CIFS mounts use that as the srcaddr. Now,
> > 	suppose I make another mount to the same server, but don't
> > 	specify the srcaddr there. That mount uses the same socket as
> > 	the first one (that is bound to the srcaddr). Now, suppose I
> > 	unmount the first mount and then fail over the IP address. What
> > 	happens to the second one? (Nothing good, I'd wager...)
> >
> > 	Perhaps we should consider using a different socket when the
> > 	srcaddr isn't specified?
> 
> The idea is that each mount with unique source addr gets it's own
> cifs session.  They will not be shared even if the server is the
> same.  If srcaddr isn't specified, then you get today's behaviour
> (shared so long as server is the same).
> 
> In your example, the bound and the un-bound shouldn't share anything,
> but it's possible my code doesn't fully do that.  If so, I would
> consider that a bug.
> 

Ok, I think that is a bug here. srcip_matches returns true when
cifs_addr_is_specified returns false. So if you don't specify a
srcaddr= option, you'll match any session, right?

> >> +		case AF_INET6:
> >> +			if (memcmp(&saddr6->sin6_addr,&vaddr6->sin6_addr,
> >> +				   sizeof(saddr6->sin6_addr)) != 0)
> >
> > 			^^^ should use ipv6_addr_equal, unless there's some reason not to do so...
> 
> addr_equal should be fine, I will change that.
> 
> >
> >> +				return false;
> >> +			break;
> >> +		}
> >> +	}
> >> +	return true;
> >> +}
> >> +
> >> +
> >> +static bool
> >> +match_address(struct TCP_Server_Info *server, struct sockaddr *addr,
> >> +	      struct sockaddr *srcaddr)
> >>   {
> >>   	struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
> >>   	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
> >> @@ -1420,6 +1467,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr)
> >>   		break;
> >>   	}
> >>
> >> +	if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr))
> >> +		return false;
> >> +
> >>   	return true;
> >>   }
> >>
> >> @@ -1487,7 +1537,8 @@ 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,
> >> +				   (struct sockaddr *)&vol->srcaddr))
> >>   			continue;
> >>
> >>   		if (!match_security(server, vol))
> >> @@ -1602,6 +1653,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
> >>   	 * no need to spinlock this init of tcpStatus or srv_count
> >>   	 */
> >>   	tcp_ses->tcpStatus = CifsNew;
> >> +	memcpy(&tcp_ses->srcaddr,&volume_info->srcaddr,
> >> +	       sizeof(tcp_ses->srcaddr));
> >>   	++tcp_ses->srv_count;
> >>
> >>   	if (addr.ss_family == AF_INET6) {
> >> @@ -1678,6 +1731,9 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
> >>   				    vol->password ? vol->password : "",
> >>   				    MAX_PASSWORD_SIZE))
> >>   				continue;
> >> +			if (!srcip_matches((struct sockaddr *)&server->srcaddr,
> >> +					   (struct sockaddr *)&ses->server->srcaddr))
> >> +				continue;
> >
> > 			^^^ there should be no need for this. By the
> > 			time you get here, you've already determined
> > 			whether you can use the same socket as another
> > 			one (via the check in match_address).
> 
> Ok, I'll remove that.
> 
> >> index ab70a3f..735989a 100644
> >> --- a/net/ipv6/addrconf.c
> >> +++ b/net/ipv6/addrconf.c
> >> @@ -230,6 +230,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
> >>
> >>   /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
> >>   const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
> >> +EXPORT_SYMBOL(in6addr_any);
> >
> > There should be no need at all to touch stuff outside of cifs.ko, at
> > least, not in this patch. If you want to do this, you need to do it as
> > a separate patch and float it in linux-netdev or someplace like that.
> >
> > In the meantime, I'd suggest declaring a private in6_addr variable and
> > initializing it to IN6ADDR_ANY_INIT (similar to what the sunrpc code
> > does).
> 
> Sounds good.  I'll see if davem will accept such a patch, and will use
> a private copy in cifs until such time as the ipv6 patch goes in.
> 
> Thanks for the review!
> 

No problem.

-- 
Jeff Layton <jlayton@xxxxxxxxx>
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux