diff options
Diffstat (limited to 'net/ipv4/inet_diag.c')
-rw-r--r-- | net/ipv4/inet_diag.c | 90 |
1 files changed, 33 insertions, 57 deletions
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 5df71cd08da8..dbeacd8b0f90 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <net/inet_hashtables.h> | 27 | #include <net/inet_hashtables.h> |
28 | #include <net/inet_timewait_sock.h> | 28 | #include <net/inet_timewait_sock.h> |
29 | #include <net/inet6_hashtables.h> | 29 | #include <net/inet6_hashtables.h> |
30 | #include <net/netlink.h> | ||
30 | 31 | ||
31 | #include <linux/inet.h> | 32 | #include <linux/inet.h> |
32 | #include <linux/stddef.h> | 33 | #include <linux/stddef.h> |
@@ -60,7 +61,7 @@ static int inet_csk_diag_fill(struct sock *sk, | |||
60 | struct nlmsghdr *nlh; | 61 | struct nlmsghdr *nlh; |
61 | void *info = NULL; | 62 | void *info = NULL; |
62 | struct inet_diag_meminfo *minfo = NULL; | 63 | struct inet_diag_meminfo *minfo = NULL; |
63 | unsigned char *b = skb->tail; | 64 | unsigned char *b = skb_tail_pointer(skb); |
64 | const struct inet_diag_handler *handler; | 65 | const struct inet_diag_handler *handler; |
65 | 66 | ||
66 | handler = inet_diag_table[unlh->nlmsg_type]; | 67 | handler = inet_diag_table[unlh->nlmsg_type]; |
@@ -147,12 +148,12 @@ static int inet_csk_diag_fill(struct sock *sk, | |||
147 | icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) | 148 | icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) |
148 | icsk->icsk_ca_ops->get_info(sk, ext, skb); | 149 | icsk->icsk_ca_ops->get_info(sk, ext, skb); |
149 | 150 | ||
150 | nlh->nlmsg_len = skb->tail - b; | 151 | nlh->nlmsg_len = skb_tail_pointer(skb) - b; |
151 | return skb->len; | 152 | return skb->len; |
152 | 153 | ||
153 | rtattr_failure: | 154 | rtattr_failure: |
154 | nlmsg_failure: | 155 | nlmsg_failure: |
155 | skb_trim(skb, b - skb->data); | 156 | nlmsg_trim(skb, b); |
156 | return -EMSGSIZE; | 157 | return -EMSGSIZE; |
157 | } | 158 | } |
158 | 159 | ||
@@ -163,7 +164,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, | |||
163 | { | 164 | { |
164 | long tmo; | 165 | long tmo; |
165 | struct inet_diag_msg *r; | 166 | struct inet_diag_msg *r; |
166 | const unsigned char *previous_tail = skb->tail; | 167 | const unsigned char *previous_tail = skb_tail_pointer(skb); |
167 | struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq, | 168 | struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq, |
168 | unlh->nlmsg_type, sizeof(*r)); | 169 | unlh->nlmsg_type, sizeof(*r)); |
169 | 170 | ||
@@ -205,10 +206,10 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, | |||
205 | &tw6->tw_v6_daddr); | 206 | &tw6->tw_v6_daddr); |
206 | } | 207 | } |
207 | #endif | 208 | #endif |
208 | nlh->nlmsg_len = skb->tail - previous_tail; | 209 | nlh->nlmsg_len = skb_tail_pointer(skb) - previous_tail; |
209 | return skb->len; | 210 | return skb->len; |
210 | nlmsg_failure: | 211 | nlmsg_failure: |
211 | skb_trim(skb, previous_tail - skb->data); | 212 | nlmsg_trim(skb, previous_tail); |
212 | return -EMSGSIZE; | 213 | return -EMSGSIZE; |
213 | } | 214 | } |
214 | 215 | ||
@@ -535,7 +536,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, | |||
535 | { | 536 | { |
536 | const struct inet_request_sock *ireq = inet_rsk(req); | 537 | const struct inet_request_sock *ireq = inet_rsk(req); |
537 | struct inet_sock *inet = inet_sk(sk); | 538 | struct inet_sock *inet = inet_sk(sk); |
538 | unsigned char *b = skb->tail; | 539 | unsigned char *b = skb_tail_pointer(skb); |
539 | struct inet_diag_msg *r; | 540 | struct inet_diag_msg *r; |
540 | struct nlmsghdr *nlh; | 541 | struct nlmsghdr *nlh; |
541 | long tmo; | 542 | long tmo; |
@@ -574,12 +575,12 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, | |||
574 | &inet6_rsk(req)->rmt_addr); | 575 | &inet6_rsk(req)->rmt_addr); |
575 | } | 576 | } |
576 | #endif | 577 | #endif |
577 | nlh->nlmsg_len = skb->tail - b; | 578 | nlh->nlmsg_len = skb_tail_pointer(skb) - b; |
578 | 579 | ||
579 | return skb->len; | 580 | return skb->len; |
580 | 581 | ||
581 | nlmsg_failure: | 582 | nlmsg_failure: |
582 | skb_trim(skb, b - skb->data); | 583 | nlmsg_trim(skb, b); |
583 | return -1; | 584 | return -1; |
584 | } | 585 | } |
585 | 586 | ||
@@ -805,68 +806,43 @@ done: | |||
805 | return skb->len; | 806 | return skb->len; |
806 | } | 807 | } |
807 | 808 | ||
808 | 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) |
809 | { | 810 | { |
810 | if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) | 811 | int hdrlen = sizeof(struct inet_diag_req); |
811 | return 0; | ||
812 | 812 | ||
813 | if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX) | 813 | if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || |
814 | goto err_inval; | 814 | nlmsg_len(nlh) < hdrlen) |
815 | return -EINVAL; | ||
815 | 816 | ||
816 | if (inet_diag_table[nlh->nlmsg_type] == NULL) | 817 | if (inet_diag_table[nlh->nlmsg_type] == NULL) |
817 | return -ENOENT; | 818 | return -ENOENT; |
818 | 819 | ||
819 | if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len) | 820 | if (nlh->nlmsg_flags & NLM_F_DUMP) { |
820 | goto err_inval; | 821 | if (nlmsg_attrlen(nlh, hdrlen)) { |
821 | 822 | struct nlattr *attr; | |
822 | if (nlh->nlmsg_flags&NLM_F_DUMP) { | 823 | |
823 | if (nlh->nlmsg_len > | 824 | attr = nlmsg_find_attr(nlh, hdrlen, |
824 | (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) { | 825 | INET_DIAG_REQ_BYTECODE); |
825 | struct rtattr *rta = (void *)(NLMSG_DATA(nlh) + | 826 | if (attr == NULL || |
826 | sizeof(struct inet_diag_req)); | 827 | nla_len(attr) < sizeof(struct inet_diag_bc_op) || |
827 | if (rta->rta_type != INET_DIAG_REQ_BYTECODE || | 828 | inet_diag_bc_audit(nla_data(attr), nla_len(attr))) |
828 | rta->rta_len < 8 || | 829 | return -EINVAL; |
829 | rta->rta_len > | ||
830 | (nlh->nlmsg_len - | ||
831 | NLMSG_SPACE(sizeof(struct inet_diag_req)))) | ||
832 | goto err_inval; | ||
833 | if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta))) | ||
834 | goto err_inval; | ||
835 | } | 830 | } |
831 | |||
836 | return netlink_dump_start(idiagnl, skb, nlh, | 832 | return netlink_dump_start(idiagnl, skb, nlh, |
837 | inet_diag_dump, NULL); | 833 | inet_diag_dump, NULL); |
838 | } else | ||
839 | return inet_diag_get_exact(skb, nlh); | ||
840 | |||
841 | err_inval: | ||
842 | return -EINVAL; | ||
843 | } | ||
844 | |||
845 | |||
846 | static inline void inet_diag_rcv_skb(struct sk_buff *skb) | ||
847 | { | ||
848 | if (skb->len >= NLMSG_SPACE(0)) { | ||
849 | int err; | ||
850 | struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; | ||
851 | |||
852 | if (nlh->nlmsg_len < sizeof(*nlh) || | ||
853 | skb->len < nlh->nlmsg_len) | ||
854 | return; | ||
855 | err = inet_diag_rcv_msg(skb, nlh); | ||
856 | if (err || nlh->nlmsg_flags & NLM_F_ACK) | ||
857 | netlink_ack(skb, nlh, err); | ||
858 | } | 834 | } |
835 | |||
836 | return inet_diag_get_exact(skb, nlh); | ||
859 | } | 837 | } |
860 | 838 | ||
861 | static void inet_diag_rcv(struct sock *sk, int len) | 839 | static void inet_diag_rcv(struct sock *sk, int len) |
862 | { | 840 | { |
863 | struct sk_buff *skb; | 841 | unsigned int qlen = 0; |
864 | unsigned int qlen = skb_queue_len(&sk->sk_receive_queue); | ||
865 | 842 | ||
866 | while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) { | 843 | do { |
867 | inet_diag_rcv_skb(skb); | 844 | netlink_run_queue(sk, &qlen, &inet_diag_rcv_msg); |
868 | kfree_skb(skb); | 845 | } while (qlen); |
869 | } | ||
870 | } | 846 | } |
871 | 847 | ||
872 | static DEFINE_SPINLOCK(inet_diag_register_lock); | 848 | static DEFINE_SPINLOCK(inet_diag_register_lock); |
@@ -917,7 +893,7 @@ static int __init inet_diag_init(void) | |||
917 | goto out; | 893 | goto out; |
918 | 894 | ||
919 | idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv, | 895 | idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv, |
920 | THIS_MODULE); | 896 | NULL, THIS_MODULE); |
921 | if (idiagnl == NULL) | 897 | if (idiagnl == NULL) |
922 | goto out_free_table; | 898 | goto out_free_table; |
923 | err = 0; | 899 | err = 0; |