nfsd4: don't check ip address in setclientid
[safe/jmp/linux-2.6] / net / ipv4 / inet_diag.c
index 7dd97c5..588a779 100644 (file)
@@ -1,8 +1,6 @@
 /*
  * inet_diag.c Module for monitoring INET transport protocols sockets.
  *
- * Version:    $Id: inet_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $
- *
  * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  *     This program is free software; you can redistribute it and/or
@@ -11,6 +9,7 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -50,6 +49,27 @@ static struct sock *idiagnl;
 #define INET_DIAG_PUT(skb, attrtype, attrlen) \
        RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
 
+static DEFINE_MUTEX(inet_diag_table_mutex);
+
+static const struct inet_diag_handler *inet_diag_lock_handler(int type)
+{
+       if (!inet_diag_table[type])
+               request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
+                              NETLINK_INET_DIAG, type);
+
+       mutex_lock(&inet_diag_table_mutex);
+       if (!inet_diag_table[type])
+               return ERR_PTR(-ENOENT);
+
+       return inet_diag_table[type];
+}
+
+static inline void inet_diag_unlock_handler(
+       const struct inet_diag_handler *handler)
+{
+       mutex_unlock(&inet_diag_table_mutex);
+}
+
 static int inet_csk_diag_fill(struct sock *sk,
                              struct sk_buff *skb,
                              int ext, u32 pid, u32 seq, u16 nlmsg_flags,
@@ -112,7 +132,7 @@ static int inet_csk_diag_fill(struct sock *sk,
        }
 #endif
 
-#define EXPIRES_IN_MS(tmo)  ((tmo - jiffies) * 1000 + HZ - 1) / HZ
+#define EXPIRES_IN_MS(tmo)  DIV_ROUND_UP((tmo - jiffies) * 1000, HZ)
 
        if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
                r->idiag_timer = 1;
@@ -190,7 +210,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
        r->id.idiag_dst[0]    = tw->tw_daddr;
        r->idiag_state        = tw->tw_substate;
        r->idiag_timer        = 3;
-       r->idiag_expires      = (tmo * 1000 + HZ - 1) / HZ;
+       r->idiag_expires      = DIV_ROUND_UP(tmo * 1000, HZ);
        r->idiag_rqueue       = 0;
        r->idiag_wqueue       = 0;
        r->idiag_uid          = 0;
@@ -234,18 +254,23 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
        struct inet_hashinfo *hashinfo;
        const struct inet_diag_handler *handler;
 
-       handler = inet_diag_table[nlh->nlmsg_type];
-       BUG_ON(handler == NULL);
+       handler = inet_diag_lock_handler(nlh->nlmsg_type);
+       if (IS_ERR(handler)) {
+               err = PTR_ERR(handler);
+               goto unlock;
+       }
+
        hashinfo = handler->idiag_hashinfo;
+       err = -EINVAL;
 
        if (req->idiag_family == AF_INET) {
-               sk = inet_lookup(hashinfo, req->id.idiag_dst[0],
+               sk = inet_lookup(&init_net, hashinfo, req->id.idiag_dst[0],
                                 req->id.idiag_dport, req->id.idiag_src[0],
                                 req->id.idiag_sport, req->id.idiag_if);
        }
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
        else if (req->idiag_family == AF_INET6) {
-               sk = inet6_lookup(hashinfo,
+               sk = inet6_lookup(&init_net, hashinfo,
                                  (struct in6_addr *)req->id.idiag_dst,
                                  req->id.idiag_dport,
                                  (struct in6_addr *)req->id.idiag_src,
@@ -254,11 +279,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
        }
 #endif
        else {
-               return -EINVAL;
+               goto unlock;
        }
 
+       err = -ENOENT;
        if (sk == NULL)
-               return -ENOENT;
+               goto unlock;
 
        err = -ESTALE;
        if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE ||
@@ -295,6 +321,8 @@ out:
                else
                        sock_put(sk);
        }
+unlock:
+       inet_diag_unlock_handler(handler);
        return err;
 }
 
@@ -677,8 +705,10 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
        const struct inet_diag_handler *handler;
        struct inet_hashinfo *hashinfo;
 
-       handler = inet_diag_table[cb->nlh->nlmsg_type];
-       BUG_ON(handler == NULL);
+       handler = inet_diag_lock_handler(cb->nlh->nlmsg_type);
+       if (IS_ERR(handler))
+               goto unlock;
+
        hashinfo = handler->idiag_hashinfo;
 
        s_i = cb->args[1];
@@ -688,13 +718,15 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
                if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
                        goto skip_listen_ht;
 
-               inet_listen_lock(hashinfo);
                for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
                        struct sock *sk;
-                       struct hlist_node *node;
+                       struct hlist_nulls_node *node;
+                       struct inet_listen_hashbucket *ilb;
 
                        num = 0;
-                       sk_for_each(sk, node, &hashinfo->listening_hash[i]) {
+                       ilb = &hashinfo->listening_hash[i];
+                       spin_lock_bh(&ilb->lock);
+                       sk_nulls_for_each(sk, node, &ilb->head) {
                                struct inet_sock *inet = inet_sk(sk);
 
                                if (num < s_num) {
@@ -712,7 +744,7 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
                                        goto syn_recv;
 
                                if (inet_csk_diag_dump(sk, skb, cb) < 0) {
-                                       inet_listen_unlock(hashinfo);
+                                       spin_unlock_bh(&ilb->lock);
                                        goto done;
                                }
 
@@ -721,7 +753,7 @@ syn_recv:
                                        goto next_listen;
 
                                if (inet_diag_dump_reqs(skb, sk, cb) < 0) {
-                                       inet_listen_unlock(hashinfo);
+                                       spin_unlock_bh(&ilb->lock);
                                        goto done;
                                }
 
@@ -730,31 +762,37 @@ next_listen:
                                cb->args[4] = 0;
                                ++num;
                        }
+                       spin_unlock_bh(&ilb->lock);
 
                        s_num = 0;
                        cb->args[3] = 0;
                        cb->args[4] = 0;
                }
-               inet_listen_unlock(hashinfo);
 skip_listen_ht:
                cb->args[0] = 1;
                s_i = num = s_num = 0;
        }
 
        if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
-               return skb->len;
+               goto unlock;
 
        for (i = s_i; i < hashinfo->ehash_size; i++) {
                struct inet_ehash_bucket *head = &hashinfo->ehash[i];
+               spinlock_t *lock = inet_ehash_lockp(hashinfo, i);
                struct sock *sk;
-               struct hlist_node *node;
+               struct hlist_nulls_node *node;
+
+               num = 0;
+
+               if (hlist_nulls_empty(&head->chain) &&
+                       hlist_nulls_empty(&head->twchain))
+                       continue;
 
                if (i > s_i)
                        s_num = 0;
 
-               read_lock_bh(&head->lock);
-               num = 0;
-               sk_for_each(sk, node, &head->chain) {
+               spin_lock_bh(lock);
+               sk_nulls_for_each(sk, node, &head->chain) {
                        struct inet_sock *inet = inet_sk(sk);
 
                        if (num < s_num)
@@ -768,7 +806,7 @@ skip_listen_ht:
                            r->id.idiag_dport)
                                goto next_normal;
                        if (inet_csk_diag_dump(sk, skb, cb) < 0) {
-                               read_unlock_bh(&head->lock);
+                               spin_unlock_bh(lock);
                                goto done;
                        }
 next_normal:
@@ -790,19 +828,21 @@ next_normal:
                                    r->id.idiag_dport)
                                        goto next_dying;
                                if (inet_twsk_diag_dump(tw, skb, cb) < 0) {
-                                       read_unlock_bh(&head->lock);
+                                       spin_unlock_bh(lock);
                                        goto done;
                                }
 next_dying:
                                ++num;
                        }
                }
-               read_unlock_bh(&head->lock);
+               spin_unlock_bh(lock);
        }
 
 done:
        cb->args[1] = i;
        cb->args[2] = num;
+unlock:
+       inet_diag_unlock_handler(handler);
        return skb->len;
 }
 
@@ -814,12 +854,7 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
            nlmsg_len(nlh) < hdrlen)
                return -EINVAL;
 
-       if (inet_diag_table[nlh->nlmsg_type] == NULL)
-               return -ENOENT;
-
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
-               int err;
-
                if (nlmsg_attrlen(nlh, hdrlen)) {
                        struct nlattr *attr;
 
@@ -831,27 +866,22 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                                return -EINVAL;
                }
 
-               err = netlink_dump_start(idiagnl, skb, nlh,
-                                        inet_diag_dump, NULL);
-               if (err == 0)
-                       err = -EINTR;
-               return err;
+               return netlink_dump_start(idiagnl, skb, nlh,
+                                         inet_diag_dump, NULL);
        }
 
        return inet_diag_get_exact(skb, nlh);
 }
 
-static void inet_diag_rcv(struct sock *sk, int len)
-{
-       unsigned int qlen = 0;
+static DEFINE_MUTEX(inet_diag_mutex);
 
-       do {
-               netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg);
-       } while (qlen);
+static void inet_diag_rcv(struct sk_buff *skb)
+{
+       mutex_lock(&inet_diag_mutex);
+       netlink_rcv_skb(skb, &inet_diag_rcv_msg);
+       mutex_unlock(&inet_diag_mutex);
 }
 
-static DEFINE_SPINLOCK(inet_diag_register_lock);
-
 int inet_diag_register(const struct inet_diag_handler *h)
 {
        const __u16 type = h->idiag_type;
@@ -860,13 +890,13 @@ int inet_diag_register(const struct inet_diag_handler *h)
        if (type >= INET_DIAG_GETSOCK_MAX)
                goto out;
 
-       spin_lock(&inet_diag_register_lock);
+       mutex_lock(&inet_diag_table_mutex);
        err = -EEXIST;
        if (inet_diag_table[type] == NULL) {
                inet_diag_table[type] = h;
                err = 0;
        }
-       spin_unlock(&inet_diag_register_lock);
+       mutex_unlock(&inet_diag_table_mutex);
 out:
        return err;
 }
@@ -879,11 +909,9 @@ void inet_diag_unregister(const struct inet_diag_handler *h)
        if (type >= INET_DIAG_GETSOCK_MAX)
                return;
 
-       spin_lock(&inet_diag_register_lock);
+       mutex_lock(&inet_diag_table_mutex);
        inet_diag_table[type] = NULL;
-       spin_unlock(&inet_diag_register_lock);
-
-       synchronize_rcu();
+       mutex_unlock(&inet_diag_table_mutex);
 }
 EXPORT_SYMBOL_GPL(inet_diag_unregister);
 
@@ -897,8 +925,8 @@ static int __init inet_diag_init(void)
        if (!inet_diag_table)
                goto out;
 
-       idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv,
-                                       THIS_MODULE);
+       idiagnl = netlink_kernel_create(&init_net, NETLINK_INET_DIAG, 0,
+                                       inet_diag_rcv, NULL, THIS_MODULE);
        if (idiagnl == NULL)
                goto out_free_table;
        err = 0;
@@ -911,10 +939,11 @@ out_free_table:
 
 static void __exit inet_diag_exit(void)
 {
-       sock_release(idiagnl->sk_socket);
+       netlink_kernel_release(idiagnl);
        kfree(inet_diag_table);
 }
 
 module_init(inet_diag_init);
 module_exit(inet_diag_exit);
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_INET_DIAG);