]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv4/inet_diag.c
[NETLINK]: Directly return -EINTR from netlink_dump_start()
[net-next-2.6.git] / net / ipv4 / inet_diag.c
index 492858e6faf0140fee625d8f966093c57c141773..0148f0e34ceba78e252acb8021385ab049a5715d 100644 (file)
@@ -27,6 +27,7 @@
 #include <net/inet_hashtables.h>
 #include <net/inet_timewait_sock.h>
 #include <net/inet6_hashtables.h>
+#include <net/netlink.h>
 
 #include <linux/inet.h>
 #include <linux/stddef.h>
@@ -36,8 +37,8 @@
 static const struct inet_diag_handler **inet_diag_table;
 
 struct inet_diag_entry {
-       u32 *saddr;
-       u32 *daddr;
+       __be32 *saddr;
+       __be32 *daddr;
        u16 sport;
        u16 dport;
        u16 family;
@@ -60,7 +61,7 @@ static int inet_csk_diag_fill(struct sock *sk,
        struct nlmsghdr  *nlh;
        void *info = NULL;
        struct inet_diag_meminfo  *minfo = NULL;
-       unsigned char    *b = skb->tail;
+       unsigned char    *b = skb_tail_pointer(skb);
        const struct inet_diag_handler *handler;
 
        handler = inet_diag_table[unlh->nlmsg_type];
@@ -147,13 +148,13 @@ static int inet_csk_diag_fill(struct sock *sk,
            icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info)
                icsk->icsk_ca_ops->get_info(sk, ext, skb);
 
-       nlh->nlmsg_len = skb->tail - b;
+       nlh->nlmsg_len = skb_tail_pointer(skb) - b;
        return skb->len;
 
 rtattr_failure:
 nlmsg_failure:
-       skb_trim(skb, b - skb->data);
-       return -1;
+       nlmsg_trim(skb, b);
+       return -EMSGSIZE;
 }
 
 static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
@@ -163,7 +164,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
 {
        long tmo;
        struct inet_diag_msg *r;
-       const unsigned char *previous_tail = skb->tail;
+       const unsigned char *previous_tail = skb_tail_pointer(skb);
        struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq,
                                         unlh->nlmsg_type, sizeof(*r));
 
@@ -205,11 +206,11 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
                               &tw6->tw_v6_daddr);
        }
 #endif
-       nlh->nlmsg_len = skb->tail - previous_tail;
+       nlh->nlmsg_len = skb_tail_pointer(skb) - previous_tail;
        return skb->len;
 nlmsg_failure:
-       skb_trim(skb, previous_tail - skb->data);
-       return -1;
+       nlmsg_trim(skb, previous_tail);
+       return -EMSGSIZE;
 }
 
 static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
@@ -274,11 +275,14 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
        if (!rep)
                goto out;
 
-       if (sk_diag_fill(sk, rep, req->idiag_ext,
-                        NETLINK_CB(in_skb).pid,
-                        nlh->nlmsg_seq, 0, nlh) <= 0)
-               BUG();
-
+       err = sk_diag_fill(sk, rep, req->idiag_ext,
+                          NETLINK_CB(in_skb).pid,
+                          nlh->nlmsg_seq, 0, nlh);
+       if (err < 0) {
+               WARN_ON(err == -EMSGSIZE);
+               kfree_skb(rep);
+               goto out;
+       }
        err = netlink_unicast(idiagnl, rep, NETLINK_CB(in_skb).pid,
                              MSG_DONTWAIT);
        if (err > 0)
@@ -294,7 +298,7 @@ out:
        return err;
 }
 
-static int bitstring_match(const u32 *a1, const u32 *a2, int bits)
+static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits)
 {
        int words = bits >> 5;
 
@@ -305,8 +309,8 @@ static int bitstring_match(const u32 *a1, const u32 *a2, int bits)
                        return 0;
        }
        if (bits) {
-               __u32 w1, w2;
-               __u32 mask;
+               __be32 w1, w2;
+               __be32 mask;
 
                w1 = a1[words];
                w2 = a2[words];
@@ -352,7 +356,7 @@ static int inet_diag_bc_run(const void *bc, int len,
                case INET_DIAG_BC_S_COND:
                case INET_DIAG_BC_D_COND: {
                        struct inet_diag_hostcond *cond;
-                       u32 *addr;
+                       __be32 *addr;
 
                        cond = (struct inet_diag_hostcond *)(op + 1);
                        if (cond->port != -1 &&
@@ -378,7 +382,7 @@ static int inet_diag_bc_run(const void *bc, int len,
                                if (addr[0] == 0 && addr[1] == 0 &&
                                    addr[2] == htonl(0xffff) &&
                                    bitstring_match(addr + 3, cond->addr,
-                                                   cond->prefix_len))
+                                                   cond->prefix_len))
                                        break;
                        }
                        yes = 0;
@@ -515,7 +519,7 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
                }
                entry.sport = tw->tw_num;
                entry.dport = ntohs(tw->tw_dport);
-               entry.userlocks = 0; 
+               entry.userlocks = 0;
 
                if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry))
                        return 0;
@@ -532,7 +536,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
 {
        const struct inet_request_sock *ireq = inet_rsk(req);
        struct inet_sock *inet = inet_sk(sk);
-       unsigned char *b = skb->tail;
+       unsigned char *b = skb_tail_pointer(skb);
        struct inet_diag_msg *r;
        struct nlmsghdr *nlh;
        long tmo;
@@ -571,12 +575,12 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
                               &inet6_rsk(req)->rmt_addr);
        }
 #endif
-       nlh->nlmsg_len = skb->tail - b;
+       nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 
        return skb->len;
 
 nlmsg_failure:
-       skb_trim(skb, b - skb->data);
+       nlmsg_trim(skb, b);
        return -1;
 }
 
@@ -775,7 +779,7 @@ next_normal:
                        struct inet_timewait_sock *tw;
 
                        inet_twsk_for_each(tw, node,
-                                   &hashinfo->ehash[i + hashinfo->ehash_size].chain) {
+                                   &head->twchain) {
 
                                if (num < s_num)
                                        goto next_dying;
@@ -802,68 +806,43 @@ done:
        return skb->len;
 }
 
-static inline int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
-       if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
-               return 0;
+       int hdrlen = sizeof(struct inet_diag_req);
 
-       if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX)
-               goto err_inval;
+       if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX ||
+           nlmsg_len(nlh) < hdrlen)
+               return -EINVAL;
 
        if (inet_diag_table[nlh->nlmsg_type] == NULL)
                return -ENOENT;
 
-       if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len)
-               goto err_inval;
-
-       if (nlh->nlmsg_flags&NLM_F_DUMP) {
-               if (nlh->nlmsg_len >
-                   (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) {
-                       struct rtattr *rta = (void *)(NLMSG_DATA(nlh) +
-                                                sizeof(struct inet_diag_req));
-                       if (rta->rta_type != INET_DIAG_REQ_BYTECODE ||
-                           rta->rta_len < 8 ||
-                           rta->rta_len >
-                           (nlh->nlmsg_len -
-                            NLMSG_SPACE(sizeof(struct inet_diag_req))))
-                               goto err_inval;
-                       if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta)))
-                               goto err_inval;
+       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+               if (nlmsg_attrlen(nlh, hdrlen)) {
+                       struct nlattr *attr;
+
+                       attr = nlmsg_find_attr(nlh, hdrlen,
+                                              INET_DIAG_REQ_BYTECODE);
+                       if (attr == NULL ||
+                           nla_len(attr) < sizeof(struct inet_diag_bc_op) ||
+                           inet_diag_bc_audit(nla_data(attr), nla_len(attr)))
+                               return -EINVAL;
                }
+
                return netlink_dump_start(idiagnl, skb, nlh,
                                          inet_diag_dump, NULL);
-       } else
-               return inet_diag_get_exact(skb, nlh);
-
-err_inval:
-       return -EINVAL;
-}
-
-
-static inline void inet_diag_rcv_skb(struct sk_buff *skb)
-{
-       if (skb->len >= NLMSG_SPACE(0)) {
-               int err;
-               struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
-
-               if (nlh->nlmsg_len < sizeof(*nlh) ||
-                   skb->len < nlh->nlmsg_len)
-                       return;
-               err = inet_diag_rcv_msg(skb, nlh);
-               if (err || nlh->nlmsg_flags & NLM_F_ACK)
-                       netlink_ack(skb, nlh, err);
        }
+
+       return inet_diag_get_exact(skb, nlh);
 }
 
 static void inet_diag_rcv(struct sock *sk, int len)
 {
-       struct sk_buff *skb;
-       unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
+       unsigned int qlen = 0;
 
-       while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) {
-               inet_diag_rcv_skb(skb);
-               kfree_skb(skb);
-       }
+       do {
+               netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg);
+       } while (qlen);
 }
 
 static DEFINE_SPINLOCK(inet_diag_register_lock);