aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv4/inet_diag.c74
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
809static inline int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 809static 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
842err_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
847static 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
862static void inet_diag_rcv(struct sock *sk, int len) 844static 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
873static DEFINE_SPINLOCK(inet_diag_register_lock); 853static DEFINE_SPINLOCK(inet_diag_register_lock);