diff options
Diffstat (limited to 'net/ipv4/inet_diag.c')
-rw-r--r-- | net/ipv4/inet_diag.c | 74 |
1 files changed, 27 insertions, 47 deletions
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 62c2e9f7e11f..7dd97c5969b3 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c | |||
@@ -806,68 +806,48 @@ done: | |||
806 | return skb->len; | 806 | return skb->len; |
807 | } | 807 | } |
808 | 808 | ||
809 | static inline int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 809 | static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) |
810 | { | 810 | { |
811 | if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) | 811 | int hdrlen = sizeof(struct inet_diag_req); |
812 | return 0; | ||
813 | 812 | ||
814 | if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX) | 813 | if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || |
815 | goto err_inval; | 814 | nlmsg_len(nlh) < hdrlen) |
815 | return -EINVAL; | ||
816 | 816 | ||
817 | if (inet_diag_table[nlh->nlmsg_type] == NULL) | 817 | if (inet_diag_table[nlh->nlmsg_type] == NULL) |
818 | return -ENOENT; | 818 | return -ENOENT; |
819 | 819 | ||
820 | if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len) | 820 | if (nlh->nlmsg_flags & NLM_F_DUMP) { |
821 | goto err_inval; | 821 | int err; |
822 | |||
823 | if (nlh->nlmsg_flags&NLM_F_DUMP) { | ||
824 | if (nlh->nlmsg_len > | ||
825 | (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) { | ||
826 | struct rtattr *rta = (void *)(NLMSG_DATA(nlh) + | ||
827 | sizeof(struct inet_diag_req)); | ||
828 | if (rta->rta_type != INET_DIAG_REQ_BYTECODE || | ||
829 | rta->rta_len < 8 || | ||
830 | rta->rta_len > | ||
831 | (nlh->nlmsg_len - | ||
832 | NLMSG_SPACE(sizeof(struct inet_diag_req)))) | ||
833 | goto err_inval; | ||
834 | if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta))) | ||
835 | goto err_inval; | ||
836 | } | ||
837 | return netlink_dump_start(idiagnl, skb, nlh, | ||
838 | inet_diag_dump, NULL); | ||
839 | } else | ||
840 | return inet_diag_get_exact(skb, nlh); | ||
841 | 822 | ||
842 | err_inval: | 823 | if (nlmsg_attrlen(nlh, hdrlen)) { |
843 | return -EINVAL; | 824 | struct nlattr *attr; |
844 | } | ||
845 | 825 | ||
826 | attr = nlmsg_find_attr(nlh, hdrlen, | ||
827 | INET_DIAG_REQ_BYTECODE); | ||
828 | if (attr == NULL || | ||
829 | nla_len(attr) < sizeof(struct inet_diag_bc_op) || | ||
830 | inet_diag_bc_audit(nla_data(attr), nla_len(attr))) | ||
831 | return -EINVAL; | ||
832 | } | ||
846 | 833 | ||
847 | static inline void inet_diag_rcv_skb(struct sk_buff *skb) | 834 | err = netlink_dump_start(idiagnl, skb, nlh, |
848 | { | 835 | inet_diag_dump, NULL); |
849 | if (skb->len >= NLMSG_SPACE(0)) { | 836 | if (err == 0) |
850 | int err; | 837 | err = -EINTR; |
851 | struct nlmsghdr *nlh = nlmsg_hdr(skb); | 838 | return err; |
852 | |||
853 | if (nlh->nlmsg_len < sizeof(*nlh) || | ||
854 | skb->len < nlh->nlmsg_len) | ||
855 | return; | ||
856 | err = inet_diag_rcv_msg(skb, nlh); | ||
857 | if (err || nlh->nlmsg_flags & NLM_F_ACK) | ||
858 | netlink_ack(skb, nlh, err); | ||
859 | } | 839 | } |
840 | |||
841 | return inet_diag_get_exact(skb, nlh); | ||
860 | } | 842 | } |
861 | 843 | ||
862 | static void inet_diag_rcv(struct sock *sk, int len) | 844 | static void inet_diag_rcv(struct sock *sk, int len) |
863 | { | 845 | { |
864 | struct sk_buff *skb; | 846 | unsigned int qlen = 0; |
865 | unsigned int qlen = skb_queue_len(&sk->sk_receive_queue); | ||
866 | 847 | ||
867 | while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) { | 848 | do { |
868 | inet_diag_rcv_skb(skb); | 849 | netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg); |
869 | kfree_skb(skb); | 850 | } while (qlen); |
870 | } | ||
871 | } | 851 | } |
872 | 852 | ||
873 | static DEFINE_SPINLOCK(inet_diag_register_lock); | 853 | static DEFINE_SPINLOCK(inet_diag_register_lock); |