diff options
Diffstat (limited to 'net/ipv4/inet_diag.c')
-rw-r--r-- | net/ipv4/inet_diag.c | 69 |
1 files changed, 45 insertions, 24 deletions
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index b0170732b5e9..e468e7a7aac4 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c | |||
@@ -51,6 +51,29 @@ static struct sock *idiagnl; | |||
51 | #define INET_DIAG_PUT(skb, attrtype, attrlen) \ | 51 | #define INET_DIAG_PUT(skb, attrtype, attrlen) \ |
52 | RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) | 52 | RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) |
53 | 53 | ||
54 | static DEFINE_MUTEX(inet_diag_table_mutex); | ||
55 | |||
56 | static const struct inet_diag_handler *inet_diag_lock_handler(int type) | ||
57 | { | ||
58 | #ifdef CONFIG_KMOD | ||
59 | if (!inet_diag_table[type]) | ||
60 | request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK, | ||
61 | NETLINK_INET_DIAG, type); | ||
62 | #endif | ||
63 | |||
64 | mutex_lock(&inet_diag_table_mutex); | ||
65 | if (!inet_diag_table[type]) | ||
66 | return ERR_PTR(-ENOENT); | ||
67 | |||
68 | return inet_diag_table[type]; | ||
69 | } | ||
70 | |||
71 | static inline void inet_diag_unlock_handler( | ||
72 | const struct inet_diag_handler *handler) | ||
73 | { | ||
74 | mutex_unlock(&inet_diag_table_mutex); | ||
75 | } | ||
76 | |||
54 | static int inet_csk_diag_fill(struct sock *sk, | 77 | static int inet_csk_diag_fill(struct sock *sk, |
55 | struct sk_buff *skb, | 78 | struct sk_buff *skb, |
56 | int ext, u32 pid, u32 seq, u16 nlmsg_flags, | 79 | int ext, u32 pid, u32 seq, u16 nlmsg_flags, |
@@ -235,9 +258,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb, | |||
235 | struct inet_hashinfo *hashinfo; | 258 | struct inet_hashinfo *hashinfo; |
236 | const struct inet_diag_handler *handler; | 259 | const struct inet_diag_handler *handler; |
237 | 260 | ||
238 | handler = inet_diag_table[nlh->nlmsg_type]; | 261 | handler = inet_diag_lock_handler(nlh->nlmsg_type); |
239 | BUG_ON(handler == NULL); | 262 | if (!handler) |
263 | return -ENOENT; | ||
264 | |||
240 | hashinfo = handler->idiag_hashinfo; | 265 | hashinfo = handler->idiag_hashinfo; |
266 | err = -EINVAL; | ||
241 | 267 | ||
242 | if (req->idiag_family == AF_INET) { | 268 | if (req->idiag_family == AF_INET) { |
243 | sk = inet_lookup(hashinfo, req->id.idiag_dst[0], | 269 | sk = inet_lookup(hashinfo, req->id.idiag_dst[0], |
@@ -255,11 +281,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb, | |||
255 | } | 281 | } |
256 | #endif | 282 | #endif |
257 | else { | 283 | else { |
258 | return -EINVAL; | 284 | goto unlock; |
259 | } | 285 | } |
260 | 286 | ||
287 | err = -ENOENT; | ||
261 | if (sk == NULL) | 288 | if (sk == NULL) |
262 | return -ENOENT; | 289 | goto unlock; |
263 | 290 | ||
264 | err = -ESTALE; | 291 | err = -ESTALE; |
265 | if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE || | 292 | if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE || |
@@ -296,6 +323,8 @@ out: | |||
296 | else | 323 | else |
297 | sock_put(sk); | 324 | sock_put(sk); |
298 | } | 325 | } |
326 | unlock: | ||
327 | inet_diag_unlock_handler(handler); | ||
299 | return err; | 328 | return err; |
300 | } | 329 | } |
301 | 330 | ||
@@ -678,8 +707,10 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
678 | const struct inet_diag_handler *handler; | 707 | const struct inet_diag_handler *handler; |
679 | struct inet_hashinfo *hashinfo; | 708 | struct inet_hashinfo *hashinfo; |
680 | 709 | ||
681 | handler = inet_diag_table[cb->nlh->nlmsg_type]; | 710 | handler = inet_diag_lock_handler(cb->nlh->nlmsg_type); |
682 | BUG_ON(handler == NULL); | 711 | if (!handler) |
712 | goto no_handler; | ||
713 | |||
683 | hashinfo = handler->idiag_hashinfo; | 714 | hashinfo = handler->idiag_hashinfo; |
684 | 715 | ||
685 | s_i = cb->args[1]; | 716 | s_i = cb->args[1]; |
@@ -743,7 +774,7 @@ skip_listen_ht: | |||
743 | } | 774 | } |
744 | 775 | ||
745 | if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV))) | 776 | if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV))) |
746 | return skb->len; | 777 | goto unlock; |
747 | 778 | ||
748 | for (i = s_i; i < hashinfo->ehash_size; i++) { | 779 | for (i = s_i; i < hashinfo->ehash_size; i++) { |
749 | struct inet_ehash_bucket *head = &hashinfo->ehash[i]; | 780 | struct inet_ehash_bucket *head = &hashinfo->ehash[i]; |
@@ -805,6 +836,9 @@ next_dying: | |||
805 | done: | 836 | done: |
806 | cb->args[1] = i; | 837 | cb->args[1] = i; |
807 | cb->args[2] = num; | 838 | cb->args[2] = num; |
839 | unlock: | ||
840 | inet_diag_unlock_handler(handler); | ||
841 | no_handler: | ||
808 | return skb->len; | 842 | return skb->len; |
809 | } | 843 | } |
810 | 844 | ||
@@ -816,15 +850,6 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
816 | nlmsg_len(nlh) < hdrlen) | 850 | nlmsg_len(nlh) < hdrlen) |
817 | return -EINVAL; | 851 | return -EINVAL; |
818 | 852 | ||
819 | #ifdef CONFIG_KMOD | ||
820 | if (inet_diag_table[nlh->nlmsg_type] == NULL) | ||
821 | request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK, | ||
822 | NETLINK_INET_DIAG, nlh->nlmsg_type); | ||
823 | #endif | ||
824 | |||
825 | if (inet_diag_table[nlh->nlmsg_type] == NULL) | ||
826 | return -ENOENT; | ||
827 | |||
828 | if (nlh->nlmsg_flags & NLM_F_DUMP) { | 853 | if (nlh->nlmsg_flags & NLM_F_DUMP) { |
829 | if (nlmsg_attrlen(nlh, hdrlen)) { | 854 | if (nlmsg_attrlen(nlh, hdrlen)) { |
830 | struct nlattr *attr; | 855 | struct nlattr *attr; |
@@ -853,8 +878,6 @@ static void inet_diag_rcv(struct sk_buff *skb) | |||
853 | mutex_unlock(&inet_diag_mutex); | 878 | mutex_unlock(&inet_diag_mutex); |
854 | } | 879 | } |
855 | 880 | ||
856 | static DEFINE_SPINLOCK(inet_diag_register_lock); | ||
857 | |||
858 | int inet_diag_register(const struct inet_diag_handler *h) | 881 | int inet_diag_register(const struct inet_diag_handler *h) |
859 | { | 882 | { |
860 | const __u16 type = h->idiag_type; | 883 | const __u16 type = h->idiag_type; |
@@ -863,13 +886,13 @@ int inet_diag_register(const struct inet_diag_handler *h) | |||
863 | if (type >= INET_DIAG_GETSOCK_MAX) | 886 | if (type >= INET_DIAG_GETSOCK_MAX) |
864 | goto out; | 887 | goto out; |
865 | 888 | ||
866 | spin_lock(&inet_diag_register_lock); | 889 | mutex_lock(&inet_diag_table_mutex); |
867 | err = -EEXIST; | 890 | err = -EEXIST; |
868 | if (inet_diag_table[type] == NULL) { | 891 | if (inet_diag_table[type] == NULL) { |
869 | inet_diag_table[type] = h; | 892 | inet_diag_table[type] = h; |
870 | err = 0; | 893 | err = 0; |
871 | } | 894 | } |
872 | spin_unlock(&inet_diag_register_lock); | 895 | mutex_unlock(&inet_diag_table_mutex); |
873 | out: | 896 | out: |
874 | return err; | 897 | return err; |
875 | } | 898 | } |
@@ -882,11 +905,9 @@ void inet_diag_unregister(const struct inet_diag_handler *h) | |||
882 | if (type >= INET_DIAG_GETSOCK_MAX) | 905 | if (type >= INET_DIAG_GETSOCK_MAX) |
883 | return; | 906 | return; |
884 | 907 | ||
885 | spin_lock(&inet_diag_register_lock); | 908 | mutex_lock(&inet_diag_table_mutex); |
886 | inet_diag_table[type] = NULL; | 909 | inet_diag_table[type] = NULL; |
887 | spin_unlock(&inet_diag_register_lock); | 910 | mutex_unlock(&inet_diag_table_mutex); |
888 | |||
889 | synchronize_rcu(); | ||
890 | } | 911 | } |
891 | EXPORT_SYMBOL_GPL(inet_diag_unregister); | 912 | EXPORT_SYMBOL_GPL(inet_diag_unregister); |
892 | 913 | ||