Commit 9dd776b6d7b0b85966b6ddd03e2b2aae59012ab1

Authored by Eric W. Biederman
Committed by David S. Miller
1 parent 8b41d1887d

[NET]: Add network namespace clone & unshare support.

This patch allows you to create a new network namespace
using sys_clone, or sys_unshare.

As the network namespace is still experimental and under development
clone and unshare support is only made available when CONFIG_NET_NS is
selected at compile time.

As this patch introduces network namespace support into code paths
that exist when the CONFIG_NET is not selected there are a few
additions made to net_namespace.h to allow a few more functions
to be used when the networking stack is not compiled in.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 6 changed files with 83 additions and 5 deletions Side-by-side Diff

include/linux/sched.h
... ... @@ -27,6 +27,7 @@
27 27 #define CLONE_NEWUTS 0x04000000 /* New utsname group? */
28 28 #define CLONE_NEWIPC 0x08000000 /* New ipcs */
29 29 #define CLONE_NEWUSER 0x10000000 /* New user namespace */
  30 +#define CLONE_NEWNET 0x20000000 /* New network namespace */
30 31  
31 32 /*
32 33 * Scheduling policies
include/net/net_namespace.h
... ... @@ -38,11 +38,23 @@
38 38  
39 39 extern struct list_head net_namespace_list;
40 40  
  41 +#ifdef CONFIG_NET
  42 +extern struct net *copy_net_ns(unsigned long flags, struct net *net_ns);
  43 +#else
  44 +static inline struct net *copy_net_ns(unsigned long flags, struct net *net_ns)
  45 +{
  46 + /* There is nothing to copy so this is a noop */
  47 + return net_ns;
  48 +}
  49 +#endif
  50 +
41 51 extern void __put_net(struct net *net);
42 52  
43 53 static inline struct net *get_net(struct net *net)
44 54 {
  55 +#ifdef CONFIG_NET
45 56 atomic_inc(&net->count);
  57 +#endif
46 58 return net;
47 59 }
48 60  
49 61  
50 62  
51 63  
52 64  
53 65  
... ... @@ -60,19 +72,25 @@
60 72  
61 73 static inline void put_net(struct net *net)
62 74 {
  75 +#ifdef CONFIG_NET
63 76 if (atomic_dec_and_test(&net->count))
64 77 __put_net(net);
  78 +#endif
65 79 }
66 80  
67 81 static inline struct net *hold_net(struct net *net)
68 82 {
  83 +#ifdef CONFIG_NET
69 84 atomic_inc(&net->use_count);
  85 +#endif
70 86 return net;
71 87 }
72 88  
73 89 static inline void release_net(struct net *net)
74 90 {
  91 +#ifdef CONFIG_NET
75 92 atomic_dec(&net->use_count);
  93 +#endif
76 94 }
77 95  
78 96 extern void net_lock(void);
... ... @@ -1608,7 +1608,8 @@
1608 1608 err = -EINVAL;
1609 1609 if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
1610 1610 CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
1611   - CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER))
  1611 + CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|
  1612 + CLONE_NEWNET))
1612 1613 goto bad_unshare_out;
1613 1614  
1614 1615 if ((err = unshare_thread(unshare_flags)))
... ... @@ -20,6 +20,7 @@
20 20 #include <linux/mnt_namespace.h>
21 21 #include <linux/utsname.h>
22 22 #include <linux/pid_namespace.h>
  23 +#include <net/net_namespace.h>
23 24  
24 25 static struct kmem_cache *nsproxy_cachep;
25 26  
26 27  
... ... @@ -98,8 +99,17 @@
98 99 goto out_user;
99 100 }
100 101  
  102 + new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);
  103 + if (IS_ERR(new_nsp->net_ns)) {
  104 + err = PTR_ERR(new_nsp->net_ns);
  105 + goto out_net;
  106 + }
  107 +
101 108 return new_nsp;
102 109  
  110 +out_net:
  111 + if (new_nsp->user_ns)
  112 + put_user_ns(new_nsp->user_ns);
103 113 out_user:
104 114 if (new_nsp->pid_ns)
105 115 put_pid_ns(new_nsp->pid_ns);
... ... @@ -132,7 +142,7 @@
132 142  
133 143 get_nsproxy(old_ns);
134 144  
135   - if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER)))
  145 + if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWNET)))
136 146 return 0;
137 147  
138 148 if (!capable(CAP_SYS_ADMIN)) {
... ... @@ -164,6 +174,7 @@
164 174 put_pid_ns(ns->pid_ns);
165 175 if (ns->user_ns)
166 176 put_user_ns(ns->user_ns);
  177 + put_net(ns->net_ns);
167 178 kmem_cache_free(nsproxy_cachep, ns);
168 179 }
169 180  
... ... @@ -177,7 +188,7 @@
177 188 int err = 0;
178 189  
179 190 if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
180   - CLONE_NEWUSER)))
  191 + CLONE_NEWUSER | CLONE_NEWNET)))
181 192 return 0;
182 193  
183 194 if (!capable(CAP_SYS_ADMIN))
... ... @@ -27,6 +27,14 @@
27 27  
28 28 menu "Networking options"
29 29  
  30 +config NET_NS
  31 + bool "Network namespace support"
  32 + default n
  33 + depends on EXPERIMENTAL && !SYSFS
  34 + help
  35 + Allow user space to create what appear to be multiple instances
  36 + of the network stack.
  37 +
30 38 source "net/packet/Kconfig"
31 39 source "net/unix/Kconfig"
32 40 source "net/xfrm/Kconfig"
net/core/net_namespace.c
... ... @@ -4,6 +4,7 @@
4 4 #include <linux/slab.h>
5 5 #include <linux/list.h>
6 6 #include <linux/delay.h>
  7 +#include <linux/sched.h>
7 8 #include <net/net_namespace.h>
8 9  
9 10 /*
10 11  
... ... @@ -32,12 +33,10 @@
32 33 mutex_unlock(&net_list_mutex);
33 34 }
34 35  
35   -#if 0
36 36 static struct net *net_alloc(void)
37 37 {
38 38 return kmem_cache_alloc(net_cachep, GFP_KERNEL);
39 39 }
40   -#endif
41 40  
42 41 static void net_free(struct net *net)
43 42 {
... ... @@ -126,6 +125,46 @@
126 125 ops->exit(net);
127 126 }
128 127 goto out;
  128 +}
  129 +
  130 +struct net *copy_net_ns(unsigned long flags, struct net *old_net)
  131 +{
  132 + struct net *new_net = NULL;
  133 + int err;
  134 +
  135 + get_net(old_net);
  136 +
  137 + if (!(flags & CLONE_NEWNET))
  138 + return old_net;
  139 +
  140 +#ifndef CONFIG_NET_NS
  141 + return ERR_PTR(-EINVAL);
  142 +#endif
  143 +
  144 + err = -ENOMEM;
  145 + new_net = net_alloc();
  146 + if (!new_net)
  147 + goto out;
  148 +
  149 + mutex_lock(&net_mutex);
  150 + err = setup_net(new_net);
  151 + if (err)
  152 + goto out_unlock;
  153 +
  154 + net_lock();
  155 + list_add_tail(&new_net->list, &net_namespace_list);
  156 + net_unlock();
  157 +
  158 +
  159 +out_unlock:
  160 + mutex_unlock(&net_mutex);
  161 +out:
  162 + put_net(old_net);
  163 + if (err) {
  164 + net_free(new_net);
  165 + new_net = ERR_PTR(err);
  166 + }
  167 + return new_net;
129 168 }
130 169  
131 170 static int __init net_ns_init(void)