diff --git a/include/linux/sched.h b/include/linux/sched.h index 313c6b6e774f887438c3926c539186ed03e6939e..a4a141055c4429f5cbe4562011c49af5b57d116c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -27,6 +27,7 @@ #define CLONE_NEWUTS 0x04000000 /* New utsname group? */ #define CLONE_NEWIPC 0x08000000 /* New ipcs */ #define CLONE_NEWUSER 0x10000000 /* New user namespace */ +#define CLONE_NEWNET 0x20000000 /* New network namespace */ /* * Scheduling policies diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index ac8f8304094ed62e25c4f35eb1a9203fe89f386c..3ea4194613edfa434f18c903510733b4ba4cc329 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -38,11 +38,23 @@ extern struct net init_net; extern struct list_head net_namespace_list; +#ifdef CONFIG_NET +extern struct net *copy_net_ns(unsigned long flags, struct net *net_ns); +#else +static inline struct net *copy_net_ns(unsigned long flags, struct net *net_ns) +{ + /* There is nothing to copy so this is a noop */ + return net_ns; +} +#endif + extern void __put_net(struct net *net); static inline struct net *get_net(struct net *net) { +#ifdef CONFIG_NET atomic_inc(&net->count); +#endif return net; } @@ -60,19 +72,25 @@ static inline struct net *maybe_get_net(struct net *net) static inline void put_net(struct net *net) { +#ifdef CONFIG_NET if (atomic_dec_and_test(&net->count)) __put_net(net); +#endif } static inline struct net *hold_net(struct net *net) { +#ifdef CONFIG_NET atomic_inc(&net->use_count); +#endif return net; } static inline void release_net(struct net *net) { +#ifdef CONFIG_NET atomic_dec(&net->use_count); +#endif } extern void net_lock(void); diff --git a/kernel/fork.c b/kernel/fork.c index 33f12f48684a0053a82d72506c1d7ec1e7ca6a15..5e67f90a1694b46fc752b0a5b48fa3701bc15ec2 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1608,7 +1608,8 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) err = -EINVAL; if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND| CLONE_VM|CLONE_FILES|CLONE_SYSVSEM| - CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER)) + CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER| + CLONE_NEWNET)) goto bad_unshare_out; if ((err = unshare_thread(unshare_flags))) diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index a4fb7d46971f48a8655ba8e5087025593cf6e79e..f1decd21a534c248054bef7c1c6f9f6ec7da072c 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -20,6 +20,7 @@ #include #include #include +#include static struct kmem_cache *nsproxy_cachep; @@ -98,8 +99,17 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, goto out_user; } + new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns); + if (IS_ERR(new_nsp->net_ns)) { + err = PTR_ERR(new_nsp->net_ns); + goto out_net; + } + return new_nsp; +out_net: + if (new_nsp->user_ns) + put_user_ns(new_nsp->user_ns); out_user: if (new_nsp->pid_ns) put_pid_ns(new_nsp->pid_ns); @@ -132,7 +142,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) get_nsproxy(old_ns); - if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER))) + if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWNET))) return 0; if (!capable(CAP_SYS_ADMIN)) { @@ -164,6 +174,7 @@ void free_nsproxy(struct nsproxy *ns) put_pid_ns(ns->pid_ns); if (ns->user_ns) put_user_ns(ns->user_ns); + put_net(ns->net_ns); kmem_cache_free(nsproxy_cachep, ns); } @@ -177,7 +188,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags, int err = 0; if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | - CLONE_NEWUSER))) + CLONE_NEWUSER | CLONE_NEWNET))) return 0; if (!capable(CAP_SYS_ADMIN)) diff --git a/net/Kconfig b/net/Kconfig index cdba08ca2efe6145464c1f55516c672dd303892c..ab4e6da5012f1e1d2ef45436fa863d80655f75e2 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -27,6 +27,14 @@ if NET menu "Networking options" +config NET_NS + bool "Network namespace support" + default n + depends on EXPERIMENTAL && !SYSFS + help + Allow user space to create what appear to be multiple instances + of the network stack. + source "net/packet/Kconfig" source "net/unix/Kconfig" source "net/xfrm/Kconfig" diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 0e6cb02d7b77dce2016c15f18147ad92a049fb9d..e478e353ea6b58f0cd09d9b4b5cae55e4b222e5a 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -4,6 +4,7 @@ #include #include #include +#include #include /* @@ -32,12 +33,10 @@ void net_unlock(void) mutex_unlock(&net_list_mutex); } -#if 0 static struct net *net_alloc(void) { return kmem_cache_alloc(net_cachep, GFP_KERNEL); } -#endif static void net_free(struct net *net) { @@ -128,6 +127,46 @@ static int setup_net(struct net *net) goto out; } +struct net *copy_net_ns(unsigned long flags, struct net *old_net) +{ + struct net *new_net = NULL; + int err; + + get_net(old_net); + + if (!(flags & CLONE_NEWNET)) + return old_net; + +#ifndef CONFIG_NET_NS + return ERR_PTR(-EINVAL); +#endif + + err = -ENOMEM; + new_net = net_alloc(); + if (!new_net) + goto out; + + mutex_lock(&net_mutex); + err = setup_net(new_net); + if (err) + goto out_unlock; + + net_lock(); + list_add_tail(&new_net->list, &net_namespace_list); + net_unlock(); + + +out_unlock: + mutex_unlock(&net_mutex); +out: + put_net(old_net); + if (err) { + net_free(new_net); + new_net = ERR_PTR(err); + } + return new_net; +} + static int __init net_ns_init(void) { int err;