]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/core/rtnetlink.c
[NETNS]: Memory leak on network namespace stop.
[linux-2.6-omap-h63xx.git] / net / core / rtnetlink.c
index 2b0b6fac6cef122e5a2be46d7b02c72a31276443..a5f4f661fa62c55427021d1becbc699519b03053 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/security.h>
 #include <linux/mutex.h>
 #include <linux/if_addr.h>
+#include <linux/nsproxy.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -59,7 +60,6 @@ struct rtnl_link
 };
 
 static DEFINE_MUTEX(rtnl_mutex);
-static struct sock *rtnl;
 
 void rtnl_lock(void)
 {
@@ -74,8 +74,6 @@ void __rtnl_unlock(void)
 void rtnl_unlock(void)
 {
        mutex_unlock(&rtnl_mutex);
-       if (rtnl && rtnl->sk_receive_queue.qlen)
-               rtnl->sk_data_ready(rtnl, 0);
        netdev_run_todo();
 }
 
@@ -306,10 +304,16 @@ EXPORT_SYMBOL_GPL(rtnl_link_register);
 void __rtnl_link_unregister(struct rtnl_link_ops *ops)
 {
        struct net_device *dev, *n;
-
-       for_each_netdev_safe(dev, n) {
-               if (dev->rtnl_link_ops == ops)
-                       ops->dellink(dev);
+       struct net *net;
+
+       for_each_net(net) {
+restart:
+               for_each_netdev_safe(net, dev, n) {
+                       if (dev->rtnl_link_ops == ops) {
+                               ops->dellink(dev);
+                               goto restart;
+                       }
+               }
        }
        list_del(&ops->list);
 }
@@ -453,8 +457,9 @@ size_t rtattr_strlcpy(char *dest, const struct rtattr *rta, size_t size)
        return ret;
 }
 
-int rtnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo)
+int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group, int echo)
 {
+       struct sock *rtnl = net->rtnl;
        int err = 0;
 
        NETLINK_CB(skb).dst_group = group;
@@ -466,14 +471,17 @@ int rtnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo)
        return err;
 }
 
-int rtnl_unicast(struct sk_buff *skb, u32 pid)
+int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid)
 {
+       struct sock *rtnl = net->rtnl;
+
        return nlmsg_unicast(rtnl, skb, pid);
 }
 
-int rtnl_notify(struct sk_buff *skb, u32 pid, u32 group,
+int rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
                struct nlmsghdr *nlh, gfp_t flags)
 {
+       struct sock *rtnl = net->rtnl;
        int report = 0;
 
        if (nlh)
@@ -482,8 +490,10 @@ int rtnl_notify(struct sk_buff *skb, u32 pid, u32 group,
        return nlmsg_notify(rtnl, skb, pid, group, report, flags);
 }
 
-void rtnl_set_sk_err(u32 group, int error)
+void rtnl_set_sk_err(struct net *net, u32 group, int error)
 {
+       struct sock *rtnl = net->rtnl;
+
        netlink_set_err(rtnl, 0, group, error);
 }
 
@@ -693,12 +703,13 @@ nla_put_failure:
 
 static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
 {
+       struct net *net = skb->sk->sk_net;
        int idx;
        int s_idx = cb->args[0];
        struct net_device *dev;
 
        idx = 0;
-       for_each_netdev(dev) {
+       for_each_netdev(net, dev) {
                if (idx < s_idx)
                        goto cont;
                if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
@@ -713,7 +724,7 @@ cont:
        return skb->len;
 }
 
-static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
+const struct nla_policy ifla_policy[IFLA_MAX+1] = {
        [IFLA_IFNAME]           = { .type = NLA_STRING, .len = IFNAMSIZ-1 },
        [IFLA_ADDRESS]          = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
        [IFLA_BROADCAST]        = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
@@ -723,6 +734,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
        [IFLA_WEIGHT]           = { .type = NLA_U32 },
        [IFLA_OPERSTATE]        = { .type = NLA_U8 },
        [IFLA_LINKMODE]         = { .type = NLA_U8 },
+       [IFLA_NET_NS_PID]       = { .type = NLA_U32 },
 };
 
 static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
@@ -730,12 +742,45 @@ static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
        [IFLA_INFO_DATA]        = { .type = NLA_NESTED },
 };
 
+static struct net *get_net_ns_by_pid(pid_t pid)
+{
+       struct task_struct *tsk;
+       struct net *net;
+
+       /* Lookup the network namespace */
+       net = ERR_PTR(-ESRCH);
+       rcu_read_lock();
+       tsk = find_task_by_vpid(pid);
+       if (tsk) {
+               struct nsproxy *nsproxy;
+               nsproxy = task_nsproxy(tsk);
+               if (nsproxy)
+                       net = get_net(nsproxy->net_ns);
+       }
+       rcu_read_unlock();
+       return net;
+}
+
 static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
                      struct nlattr **tb, char *ifname, int modified)
 {
        int send_addr_notify = 0;
        int err;
 
+       if (tb[IFLA_NET_NS_PID]) {
+               struct net *net;
+               net = get_net_ns_by_pid(nla_get_u32(tb[IFLA_NET_NS_PID]));
+               if (IS_ERR(net)) {
+                       err = PTR_ERR(net);
+                       goto errout;
+               }
+               err = dev_change_net_namespace(dev, net, ifname);
+               put_net(net);
+               if (err)
+                       goto errout;
+               modified = 1;
+       }
+
        if (tb[IFLA_MAP]) {
                struct rtnl_link_ifmap *u_map;
                struct ifmap k_map;
@@ -858,6 +903,7 @@ errout:
 
 static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
+       struct net *net = skb->sk->sk_net;
        struct ifinfomsg *ifm;
        struct net_device *dev;
        int err;
@@ -876,9 +922,9 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        err = -EINVAL;
        ifm = nlmsg_data(nlh);
        if (ifm->ifi_index > 0)
-               dev = dev_get_by_index(ifm->ifi_index);
+               dev = dev_get_by_index(net, ifm->ifi_index);
        else if (tb[IFLA_IFNAME])
-               dev = dev_get_by_name(ifname);
+               dev = dev_get_by_name(net, ifname);
        else
                goto errout;
 
@@ -904,6 +950,7 @@ errout:
 
 static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
+       struct net *net = skb->sk->sk_net;
        const struct rtnl_link_ops *ops;
        struct net_device *dev;
        struct ifinfomsg *ifm;
@@ -920,9 +967,9 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 
        ifm = nlmsg_data(nlh);
        if (ifm->ifi_index > 0)
-               dev = __dev_get_by_index(ifm->ifi_index);
+               dev = __dev_get_by_index(net, ifm->ifi_index);
        else if (tb[IFLA_IFNAME])
-               dev = __dev_get_by_name(ifname);
+               dev = __dev_get_by_name(net, ifname);
        else
                return -EINVAL;
 
@@ -937,8 +984,52 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        return 0;
 }
 
+struct net_device *rtnl_create_link(struct net *net, char *ifname,
+               const struct rtnl_link_ops *ops, struct nlattr *tb[])
+{
+       int err;
+       struct net_device *dev;
+
+       err = -ENOMEM;
+       dev = alloc_netdev(ops->priv_size, ifname, ops->setup);
+       if (!dev)
+               goto err;
+
+       if (strchr(dev->name, '%')) {
+               err = dev_alloc_name(dev, dev->name);
+               if (err < 0)
+                       goto err_free;
+       }
+
+       dev->nd_net = net;
+       dev->rtnl_link_ops = ops;
+
+       if (tb[IFLA_MTU])
+               dev->mtu = nla_get_u32(tb[IFLA_MTU]);
+       if (tb[IFLA_ADDRESS])
+               memcpy(dev->dev_addr, nla_data(tb[IFLA_ADDRESS]),
+                               nla_len(tb[IFLA_ADDRESS]));
+       if (tb[IFLA_BROADCAST])
+               memcpy(dev->broadcast, nla_data(tb[IFLA_BROADCAST]),
+                               nla_len(tb[IFLA_BROADCAST]));
+       if (tb[IFLA_TXQLEN])
+               dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
+       if (tb[IFLA_OPERSTATE])
+               set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
+       if (tb[IFLA_LINKMODE])
+               dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
+
+       return dev;
+
+err_free:
+       free_netdev(dev);
+err:
+       return ERR_PTR(err);
+}
+
 static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
+       struct net *net = skb->sk->sk_net;
        const struct rtnl_link_ops *ops;
        struct net_device *dev;
        struct ifinfomsg *ifm;
@@ -962,9 +1053,9 @@ replay:
 
        ifm = nlmsg_data(nlh);
        if (ifm->ifi_index > 0)
-               dev = __dev_get_by_index(ifm->ifi_index);
+               dev = __dev_get_by_index(net, ifm->ifi_index);
        else if (ifname[0])
-               dev = __dev_get_by_name(ifname);
+               dev = __dev_get_by_name(net, ifname);
        else
                dev = NULL;
 
@@ -1049,38 +1140,17 @@ replay:
 
                if (!ifname[0])
                        snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
-               dev = alloc_netdev(ops->priv_size, ifname, ops->setup);
-               if (!dev)
-                       return -ENOMEM;
-
-               if (strchr(dev->name, '%')) {
-                       err = dev_alloc_name(dev, dev->name);
-                       if (err < 0)
-                               goto err_free;
-               }
-               dev->rtnl_link_ops = ops;
-
-               if (tb[IFLA_MTU])
-                       dev->mtu = nla_get_u32(tb[IFLA_MTU]);
-               if (tb[IFLA_ADDRESS])
-                       memcpy(dev->dev_addr, nla_data(tb[IFLA_ADDRESS]),
-                              nla_len(tb[IFLA_ADDRESS]));
-               if (tb[IFLA_BROADCAST])
-                       memcpy(dev->broadcast, nla_data(tb[IFLA_BROADCAST]),
-                              nla_len(tb[IFLA_BROADCAST]));
-               if (tb[IFLA_TXQLEN])
-                       dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
-               if (tb[IFLA_OPERSTATE])
-                       set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
-               if (tb[IFLA_LINKMODE])
-                       dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
-
-               if (ops->newlink)
+
+               dev = rtnl_create_link(net, ifname, ops, tb);
+
+               if (IS_ERR(dev))
+                       err = PTR_ERR(dev);
+               else if (ops->newlink)
                        err = ops->newlink(dev, tb, data);
                else
                        err = register_netdevice(dev);
-err_free:
-               if (err < 0)
+
+               if (err < 0 && !IS_ERR(dev))
                        free_netdev(dev);
                return err;
        }
@@ -1088,6 +1158,7 @@ err_free:
 
 static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
+       struct net *net = skb->sk->sk_net;
        struct ifinfomsg *ifm;
        struct nlattr *tb[IFLA_MAX+1];
        struct net_device *dev = NULL;
@@ -1100,7 +1171,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 
        ifm = nlmsg_data(nlh);
        if (ifm->ifi_index > 0) {
-               dev = dev_get_by_index(ifm->ifi_index);
+               dev = dev_get_by_index(net, ifm->ifi_index);
                if (dev == NULL)
                        return -ENODEV;
        } else
@@ -1120,7 +1191,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
                kfree_skb(nskb);
                goto errout;
        }
-       err = rtnl_unicast(nskb, NETLINK_CB(skb).pid);
+       err = rtnl_unicast(nskb, net, NETLINK_CB(skb).pid);
 errout:
        dev_put(dev);
 
@@ -1153,6 +1224,7 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
 
 void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
 {
+       struct net *net = dev->nd_net;
        struct sk_buff *skb;
        int err = -ENOBUFS;
 
@@ -1167,10 +1239,10 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+       err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
 errout:
        if (err < 0)
-               rtnl_set_sk_err(RTNLGRP_LINK, err);
+               rtnl_set_sk_err(net, RTNLGRP_LINK, err);
 }
 
 /* Protected by RTNL sempahore.  */
@@ -1181,6 +1253,7 @@ static int rtattr_max;
 
 static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
+       struct net *net = skb->sk->sk_net;
        rtnl_doit_func doit;
        int sz_idx, kind;
        int min_len;
@@ -1209,6 +1282,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                return -EPERM;
 
        if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
+               struct sock *rtnl;
                rtnl_dumpit_func dumpit;
 
                dumpit = rtnl_get_dumpit(family, type);
@@ -1216,6 +1290,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                        return -EOPNOTSUPP;
 
                __rtnl_unlock();
+               rtnl = net->rtnl;
                err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL);
                rtnl_lock();
                return err;
@@ -1249,22 +1324,17 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        return doit(skb, nlh, (void *)&rta_buf[0]);
 }
 
-static void rtnetlink_rcv(struct sock *sk, int len)
+static void rtnetlink_rcv(struct sk_buff *skb)
 {
-       unsigned int qlen = 0;
-
-       do {
-               mutex_lock(&rtnl_mutex);
-               netlink_run_queue(sk, &qlen, &rtnetlink_rcv_msg);
-               mutex_unlock(&rtnl_mutex);
-
-               netdev_run_todo();
-       } while (qlen);
+       rtnl_lock();
+       netlink_rcv_skb(skb, &rtnetlink_rcv_msg);
+       rtnl_unlock();
 }
 
 static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
        struct net_device *dev = ptr;
+
        switch (event) {
        case NETDEV_UNREGISTER:
                rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
@@ -1290,6 +1360,40 @@ static struct notifier_block rtnetlink_dev_notifier = {
        .notifier_call  = rtnetlink_event,
 };
 
+
+static int rtnetlink_net_init(struct net *net)
+{
+       struct sock *sk;
+       sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX,
+                                  rtnetlink_rcv, &rtnl_mutex, THIS_MODULE);
+       if (!sk)
+               return -ENOMEM;
+
+       /* Don't hold an extra reference on the namespace */
+       put_net(sk->sk_net);
+       net->rtnl = sk;
+       return 0;
+}
+
+static void rtnetlink_net_exit(struct net *net)
+{
+       struct sock *sk = net->rtnl;
+       if (sk) {
+               /* At the last minute lie and say this is a socket for the
+                * initial network namespace.  So the socket will be safe to
+                * free.
+                */
+               sk->sk_net = get_net(&init_net);
+               sock_release(net->rtnl->sk_socket);
+               net->rtnl = NULL;
+       }
+}
+
+static struct pernet_operations rtnetlink_net_ops = {
+       .init = rtnetlink_net_init,
+       .exit = rtnetlink_net_exit,
+};
+
 void __init rtnetlink_init(void)
 {
        int i;
@@ -1302,10 +1406,9 @@ void __init rtnetlink_init(void)
        if (!rta_buf)
                panic("rtnetlink_init: cannot allocate rta_buf\n");
 
-       rtnl = netlink_kernel_create(NETLINK_ROUTE, RTNLGRP_MAX, rtnetlink_rcv,
-                                    &rtnl_mutex, THIS_MODULE);
-       if (rtnl == NULL)
+       if (register_pernet_subsys(&rtnetlink_net_ops))
                panic("rtnetlink_init: cannot initialize rtnetlink\n");
+
        netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV);
        register_netdevice_notifier(&rtnetlink_dev_notifier);
 
@@ -1329,3 +1432,5 @@ EXPORT_SYMBOL(rtnl_unlock);
 EXPORT_SYMBOL(rtnl_unicast);
 EXPORT_SYMBOL(rtnl_notify);
 EXPORT_SYMBOL(rtnl_set_sk_err);
+EXPORT_SYMBOL(rtnl_create_link);
+EXPORT_SYMBOL(ifla_policy);