]> 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 4a2640d3826166d98b066d626f9427e5daacc256..a5f4f661fa62c55427021d1becbc699519b03053 100644 (file)
@@ -60,7 +60,6 @@ struct rtnl_link
 };
 
 static DEFINE_MUTEX(rtnl_mutex);
-static struct sock *rtnl;
 
 void rtnl_lock(void)
 {
@@ -308,9 +307,12 @@ void __rtnl_link_unregister(struct rtnl_link_ops *ops)
        struct net *net;
 
        for_each_net(net) {
+restart:
                for_each_netdev_safe(net, dev, n) {
-                       if (dev->rtnl_link_ops == ops)
+                       if (dev->rtnl_link_ops == ops) {
                                ops->dellink(dev);
+                               goto restart;
+                       }
                }
        }
        list_del(&ops->list);
@@ -455,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;
@@ -468,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)
@@ -484,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);
 }
 
@@ -742,7 +750,7 @@ static struct net *get_net_ns_by_pid(pid_t pid)
        /* Lookup the network namespace */
        net = ERR_PTR(-ESRCH);
        rcu_read_lock();
-       tsk = find_task_by_pid(pid);
+       tsk = find_task_by_vpid(pid);
        if (tsk) {
                struct nsproxy *nsproxy;
                nsproxy = task_nsproxy(tsk);
@@ -1183,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);
 
@@ -1216,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;
 
@@ -1230,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.  */
@@ -1244,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;
@@ -1272,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);
@@ -1279,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;
@@ -1323,9 +1335,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi
 {
        struct net_device *dev = ptr;
 
-       if (dev->nd_net != &init_net)
-               return NOTIFY_DONE;
-
        switch (event) {
        case NETDEV_UNREGISTER:
                rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
@@ -1351,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;
@@ -1363,10 +1406,9 @@ void __init rtnetlink_init(void)
        if (!rta_buf)
                panic("rtnetlink_init: cannot allocate rta_buf\n");
 
-       rtnl = netlink_kernel_create(&init_net, 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);