Make CIFS mount work in a container.

Teach cifs about network namespaces, so mounting uses adresses/routing
visible from the container rather than from init context.

A container is a chroot on steroids that changes more than just the root
filesystem the new processes see.  One thing containers can isolate is
"network namespaces", meaning each container can have its own set of
ethernet interfaces, each with its own own IP address and routing to the
outside world.  And if you open a socket in _userspace_ from processes
within such a container, this works fine.

But sockets opened from within the kernel still use a single global
networking context in a lot of places, meaning the new socket's address
and routing are correct for PID 1 on the host, but are _not_ what
userspace processes in the container get to use.

So when you mount a network filesystem from within in a container, the
mount code in the CIFS driver uses the host's networking context and not
the container's networking context, so it gets the wrong address, uses
the wrong routing, and may even try to go out an interface that the
container can't even access...  Bad stuff.

This patch copies the mount process's network context into the CIFS
structure that stores the rest of the server information for that mount
point, and changes the socket open code to use the saved network context
instead of the global network context.  I.E. "when you attempt to use
these addresses, do so relative to THIS set of network interfaces and
routing rules, not the old global context from back before we supported
containers".

The big long HOWTO sets up a test environment on the assumption you've
never used ocntainers before.  It basically says:

1) configure and build a new kernel that has container support
2) build a new root filesystem that includes the userspace container
control package (LXC)
3) package/run them under KVM (so you don't have to mess up your host
system in order to play with containers).
4) set up some containers under the KVM system
5) set up contradictory routing in the KVM system and the container so
that the host and the container see different things for the same address
6) try to mount a CIFS share from both contexts so you can both force it
to work and force it to fail.

For a long drawn out test reproduction sequence, see:

  http://landley.livejournal.com/47024.html
  http://landley.livejournal.com/47205.html
  http://landley.livejournal.com/47476.html

Signed-off-by: Rob Landley <rlandley@parallels.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
Rob Landley 2011-01-22 15:44:05 -06:00 committed by Steve French
parent 3f391c79b0
commit f1d0c99865
2 changed files with 43 additions and 2 deletions

View File

@ -166,6 +166,9 @@ struct TCP_Server_Info {
struct socket *ssocket;
struct sockaddr_storage dstaddr;
struct sockaddr_storage srcaddr; /* locally bind to this IP */
#ifdef CONFIG_NET_NS
struct net *net;
#endif
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;
@ -216,6 +219,36 @@ struct TCP_Server_Info {
#endif
};
/*
* Macros to allow the TCP_Server_Info->net field and related code to drop out
* when CONFIG_NET_NS isn't set.
*/
#ifdef CONFIG_NET_NS
static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv)
{
return srv->net;
}
static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net)
{
srv->net = net;
}
#else
static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv)
{
return &init_net;
}
static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net)
{
}
#endif
/*
* Session structure. One of these for each uid session with a particular host
*/

View File

@ -1568,6 +1568,9 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol)
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
continue;
if (!match_address(server, addr,
(struct sockaddr *)&vol->srcaddr))
continue;
@ -1598,6 +1601,8 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
return;
}
put_net(cifs_net_ns(server));
list_del_init(&server->tcp_ses_list);
spin_unlock(&cifs_tcp_ses_lock);
@ -1672,6 +1677,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
goto out_err;
}
cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
tcp_ses->hostname = extract_hostname(volume_info->UNC);
if (IS_ERR(tcp_ses->hostname)) {
rc = PTR_ERR(tcp_ses->hostname);
@ -1752,6 +1758,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
out_err_crypto_release:
cifs_crypto_shash_release(tcp_ses);
put_net(cifs_net_ns(tcp_ses));
out_err:
if (tcp_ses) {
if (!IS_ERR(tcp_ses->hostname))
@ -2263,8 +2271,8 @@ generic_ip_connect(struct TCP_Server_Info *server)
}
if (socket == NULL) {
rc = sock_create_kern(sfamily, SOCK_STREAM,
IPPROTO_TCP, &socket);
rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM,
IPPROTO_TCP, &socket, 1);
if (rc < 0) {
cERROR(1, "Error %d creating socket", rc);
server->ssocket = NULL;