Skip to content

Commit

Permalink
Make CIFS mount work in a container.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Rob Landley authored and Steve French committed Jan 24, 2011
1 parent 3f391c7 commit f1d0c99
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 2 deletions.
33 changes: 33 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*/
Expand Down
12 changes: 10 additions & 2 deletions fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit f1d0c99

Please sign in to comment.