]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - net/core/net_namespace.c
Merge tag 'mvebu-fixes-4.13-1' of git://git.infradead.org/linux-mvebu into fixes
[karo-tx-linux.git] / net / core / net_namespace.c
index 26bbfababff27cecc589bca69e3eb14739187f5b..8726d051f31dd2ddce96a8cd7b1ccf50435f0d92 100644 (file)
@@ -284,7 +284,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
        LIST_HEAD(net_exit_list);
 
        atomic_set(&net->count, 1);
-       atomic_set(&net->passive, 1);
+       refcount_set(&net->passive, 1);
        net->dev_base_seq = 1;
        net->user_ns = user_ns;
        idr_init(&net->netns_ids);
@@ -380,7 +380,7 @@ static void net_free(struct net *net)
 void net_drop_ns(void *p)
 {
        struct net *ns = p;
-       if (ns && atomic_dec_and_test(&ns->passive))
+       if (ns && refcount_dec_and_test(&ns->passive))
                net_free(ns);
 }
 
@@ -501,6 +501,23 @@ static void cleanup_net(struct work_struct *work)
                net_drop_ns(net);
        }
 }
+
+/**
+ * net_ns_barrier - wait until concurrent net_cleanup_work is done
+ *
+ * cleanup_net runs from work queue and will first remove namespaces
+ * from the global list, then run net exit functions.
+ *
+ * Call this in module exit path to make sure that all netns
+ * ->exit ops have been invoked before the function is removed.
+ */
+void net_ns_barrier(void)
+{
+       mutex_lock(&net_mutex);
+       mutex_unlock(&net_mutex);
+}
+EXPORT_SYMBOL(net_ns_barrier);
+
 static DECLARE_WORK(net_cleanup_work, cleanup_net);
 
 void __put_net(struct net *net)
@@ -596,6 +613,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[NETNSA_MAX + 1];
+       struct nlattr *nla;
        struct net *peer;
        int nsid, err;
 
@@ -603,23 +621,35 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
                          rtnl_net_policy, extack);
        if (err < 0)
                return err;
-       if (!tb[NETNSA_NSID])
+       if (!tb[NETNSA_NSID]) {
+               NL_SET_ERR_MSG(extack, "nsid is missing");
                return -EINVAL;
+       }
        nsid = nla_get_s32(tb[NETNSA_NSID]);
 
-       if (tb[NETNSA_PID])
+       if (tb[NETNSA_PID]) {
                peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID]));
-       else if (tb[NETNSA_FD])
+               nla = tb[NETNSA_PID];
+       } else if (tb[NETNSA_FD]) {
                peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD]));
-       else
+               nla = tb[NETNSA_FD];
+       } else {
+               NL_SET_ERR_MSG(extack, "Peer netns reference is missing");
                return -EINVAL;
-       if (IS_ERR(peer))
+       }
+       if (IS_ERR(peer)) {
+               NL_SET_BAD_ATTR(extack, nla);
+               NL_SET_ERR_MSG(extack, "Peer netns reference is invalid");
                return PTR_ERR(peer);
+       }
 
        spin_lock_bh(&net->nsid_lock);
        if (__peernet2id(net, peer) >= 0) {
                spin_unlock_bh(&net->nsid_lock);
                err = -EEXIST;
+               NL_SET_BAD_ATTR(extack, nla);
+               NL_SET_ERR_MSG(extack,
+                              "Peer netns already has a nsid assigned");
                goto out;
        }
 
@@ -628,6 +658,10 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (err >= 0) {
                rtnl_net_notifyid(net, RTM_NEWNSID, err);
                err = 0;
+       } else if (err == -ENOSPC && nsid >= 0) {
+               err = -EEXIST;
+               NL_SET_BAD_ATTR(extack, tb[NETNSA_NSID]);
+               NL_SET_ERR_MSG(extack, "The specified nsid is already used");
        }
 out:
        put_net(peer);
@@ -670,6 +704,7 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[NETNSA_MAX + 1];
+       struct nlattr *nla;
        struct sk_buff *msg;
        struct net *peer;
        int err, id;
@@ -678,15 +713,22 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
                          rtnl_net_policy, extack);
        if (err < 0)
                return err;
-       if (tb[NETNSA_PID])
+       if (tb[NETNSA_PID]) {
                peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID]));
-       else if (tb[NETNSA_FD])
+               nla = tb[NETNSA_PID];
+       } else if (tb[NETNSA_FD]) {
                peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD]));
-       else
+               nla = tb[NETNSA_FD];
+       } else {
+               NL_SET_ERR_MSG(extack, "Peer netns reference is missing");
                return -EINVAL;
+       }
 
-       if (IS_ERR(peer))
+       if (IS_ERR(peer)) {
+               NL_SET_BAD_ATTR(extack, nla);
+               NL_SET_ERR_MSG(extack, "Peer netns reference is invalid");
                return PTR_ERR(peer);
+       }
 
        msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL);
        if (!msg) {