diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/Kconfig | 3 | ||||
-rw-r--r-- | net/ipv4/tcp_diag.c | 153 |
2 files changed, 111 insertions, 45 deletions
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 960c02faf440..1e6db2a896b9 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig | |||
@@ -423,9 +423,6 @@ config IP_TCPDIAG | |||
423 | 423 | ||
424 | If unsure, say Y. | 424 | If unsure, say Y. |
425 | 425 | ||
426 | config IP_TCPDIAG_DCCP | ||
427 | def_bool (IP_TCPDIAG=y && IP_DCCP=y) || (IP_TCPDIAG=m && IP_DCCP) | ||
428 | |||
429 | config TCP_CONG_ADVANCED | 426 | config TCP_CONG_ADVANCED |
430 | bool "TCP: advanced congestion control" | 427 | bool "TCP: advanced congestion control" |
431 | ---help--- | 428 | ---help--- |
diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index b812191b2f5c..b13b71cb9ced 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c | |||
@@ -34,6 +34,8 @@ | |||
34 | 34 | ||
35 | #include <linux/tcp_diag.h> | 35 | #include <linux/tcp_diag.h> |
36 | 36 | ||
37 | static const struct inet_diag_handler **inet_diag_table; | ||
38 | |||
37 | struct tcpdiag_entry | 39 | struct tcpdiag_entry |
38 | { | 40 | { |
39 | u32 *saddr; | 41 | u32 *saddr; |
@@ -61,18 +63,24 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, | |||
61 | const struct inet_connection_sock *icsk = inet_csk(sk); | 63 | const struct inet_connection_sock *icsk = inet_csk(sk); |
62 | struct tcpdiagmsg *r; | 64 | struct tcpdiagmsg *r; |
63 | struct nlmsghdr *nlh; | 65 | struct nlmsghdr *nlh; |
64 | struct tcp_info *info = NULL; | 66 | void *info = NULL; |
65 | struct tcpdiag_meminfo *minfo = NULL; | 67 | struct tcpdiag_meminfo *minfo = NULL; |
66 | unsigned char *b = skb->tail; | 68 | unsigned char *b = skb->tail; |
69 | const struct inet_diag_handler *handler; | ||
70 | |||
71 | handler = inet_diag_table[unlh->nlmsg_type]; | ||
72 | BUG_ON(handler == NULL); | ||
67 | 73 | ||
68 | nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); | 74 | nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); |
69 | nlh->nlmsg_flags = nlmsg_flags; | 75 | nlh->nlmsg_flags = nlmsg_flags; |
76 | |||
70 | r = NLMSG_DATA(nlh); | 77 | r = NLMSG_DATA(nlh); |
71 | if (sk->sk_state != TCP_TIME_WAIT) { | 78 | if (sk->sk_state != TCP_TIME_WAIT) { |
72 | if (ext & (1<<(TCPDIAG_MEMINFO-1))) | 79 | if (ext & (1<<(TCPDIAG_MEMINFO-1))) |
73 | minfo = TCPDIAG_PUT(skb, TCPDIAG_MEMINFO, sizeof(*minfo)); | 80 | minfo = TCPDIAG_PUT(skb, TCPDIAG_MEMINFO, sizeof(*minfo)); |
74 | if (ext & (1<<(TCPDIAG_INFO-1))) | 81 | if (ext & (1<<(TCPDIAG_INFO-1))) |
75 | info = TCPDIAG_PUT(skb, TCPDIAG_INFO, sizeof(*info)); | 82 | info = TCPDIAG_PUT(skb, TCPDIAG_INFO, |
83 | handler->idiag_info_size); | ||
76 | 84 | ||
77 | if ((ext & (1 << (TCPDIAG_CONG - 1))) && icsk->icsk_ca_ops) { | 85 | if ((ext & (1 << (TCPDIAG_CONG - 1))) && icsk->icsk_ca_ops) { |
78 | size_t len = strlen(icsk->icsk_ca_ops->name); | 86 | size_t len = strlen(icsk->icsk_ca_ops->name); |
@@ -155,19 +163,6 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, | |||
155 | r->tcpdiag_expires = 0; | 163 | r->tcpdiag_expires = 0; |
156 | } | 164 | } |
157 | #undef EXPIRES_IN_MS | 165 | #undef EXPIRES_IN_MS |
158 | /* | ||
159 | * Ahem... for now we'll have some knowledge about TCP -acme | ||
160 | * But this is just one of two small exceptions, both in this | ||
161 | * function, so lets close our eyes for some 15 lines or so... 8) | ||
162 | * -acme | ||
163 | */ | ||
164 | if (sk->sk_protocol == IPPROTO_TCP) { | ||
165 | const struct tcp_sock *tp = tcp_sk(sk); | ||
166 | |||
167 | r->tcpdiag_rqueue = tp->rcv_nxt - tp->copied_seq; | ||
168 | r->tcpdiag_wqueue = tp->write_seq - tp->snd_una; | ||
169 | } else | ||
170 | r->tcpdiag_rqueue = r->tcpdiag_wqueue = 0; | ||
171 | 166 | ||
172 | r->tcpdiag_uid = sock_i_uid(sk); | 167 | r->tcpdiag_uid = sock_i_uid(sk); |
173 | r->tcpdiag_inode = sock_i_ino(sk); | 168 | r->tcpdiag_inode = sock_i_ino(sk); |
@@ -179,13 +174,7 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, | |||
179 | minfo->tcpdiag_tmem = atomic_read(&sk->sk_wmem_alloc); | 174 | minfo->tcpdiag_tmem = atomic_read(&sk->sk_wmem_alloc); |
180 | } | 175 | } |
181 | 176 | ||
182 | /* Ahem... for now we'll have some knowledge about TCP -acme */ | 177 | handler->idiag_get_info(sk, r, info); |
183 | if (info) { | ||
184 | if (sk->sk_protocol == IPPROTO_TCP) | ||
185 | tcp_get_info(sk, info); | ||
186 | else | ||
187 | memset(info, 0, sizeof(*info)); | ||
188 | } | ||
189 | 178 | ||
190 | if (sk->sk_state < TCP_TIME_WAIT && | 179 | if (sk->sk_state < TCP_TIME_WAIT && |
191 | icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) | 180 | icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) |
@@ -206,11 +195,13 @@ static int tcpdiag_get_exact(struct sk_buff *in_skb, const struct nlmsghdr *nlh) | |||
206 | struct sock *sk; | 195 | struct sock *sk; |
207 | struct tcpdiagreq *req = NLMSG_DATA(nlh); | 196 | struct tcpdiagreq *req = NLMSG_DATA(nlh); |
208 | struct sk_buff *rep; | 197 | struct sk_buff *rep; |
209 | struct inet_hashinfo *hashinfo = &tcp_hashinfo; | 198 | struct inet_hashinfo *hashinfo; |
210 | #ifdef CONFIG_IP_TCPDIAG_DCCP | 199 | const struct inet_diag_handler *handler; |
211 | if (nlh->nlmsg_type == DCCPDIAG_GETSOCK) | 200 | |
212 | hashinfo = &dccp_hashinfo; | 201 | handler = inet_diag_table[nlh->nlmsg_type]; |
213 | #endif | 202 | BUG_ON(handler == NULL); |
203 | hashinfo = handler->idiag_hashinfo; | ||
204 | |||
214 | if (req->tcpdiag_family == AF_INET) { | 205 | if (req->tcpdiag_family == AF_INET) { |
215 | sk = inet_lookup(hashinfo, req->id.tcpdiag_dst[0], | 206 | sk = inet_lookup(hashinfo, req->id.tcpdiag_dst[0], |
216 | req->id.tcpdiag_dport, req->id.tcpdiag_src[0], | 207 | req->id.tcpdiag_dport, req->id.tcpdiag_src[0], |
@@ -241,9 +232,10 @@ static int tcpdiag_get_exact(struct sk_buff *in_skb, const struct nlmsghdr *nlh) | |||
241 | goto out; | 232 | goto out; |
242 | 233 | ||
243 | err = -ENOMEM; | 234 | err = -ENOMEM; |
244 | rep = alloc_skb(NLMSG_SPACE(sizeof(struct tcpdiagmsg)+ | 235 | rep = alloc_skb(NLMSG_SPACE((sizeof(struct tcpdiagmsg) + |
245 | sizeof(struct tcpdiag_meminfo)+ | 236 | sizeof(struct tcpdiag_meminfo) + |
246 | sizeof(struct tcp_info)+64), GFP_KERNEL); | 237 | handler->idiag_info_size + 64)), |
238 | GFP_KERNEL); | ||
247 | if (!rep) | 239 | if (!rep) |
248 | goto out; | 240 | goto out; |
249 | 241 | ||
@@ -603,15 +595,16 @@ static int tcpdiag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
603 | int i, num; | 595 | int i, num; |
604 | int s_i, s_num; | 596 | int s_i, s_num; |
605 | struct tcpdiagreq *r = NLMSG_DATA(cb->nlh); | 597 | struct tcpdiagreq *r = NLMSG_DATA(cb->nlh); |
598 | const struct inet_diag_handler *handler; | ||
606 | struct inet_hashinfo *hashinfo; | 599 | struct inet_hashinfo *hashinfo; |
607 | 600 | ||
601 | handler = inet_diag_table[cb->nlh->nlmsg_type]; | ||
602 | BUG_ON(handler == NULL); | ||
603 | hashinfo = handler->idiag_hashinfo; | ||
604 | |||
608 | s_i = cb->args[1]; | 605 | s_i = cb->args[1]; |
609 | s_num = num = cb->args[2]; | 606 | s_num = num = cb->args[2]; |
610 | hashinfo = &tcp_hashinfo; | 607 | |
611 | #ifdef CONFIG_IP_TCPDIAG_DCCP | ||
612 | if (cb->nlh->nlmsg_type == DCCPDIAG_GETSOCK) | ||
613 | hashinfo = &dccp_hashinfo; | ||
614 | #endif | ||
615 | if (cb->args[0] == 0) { | 608 | if (cb->args[0] == 0) { |
616 | if (!(r->tcpdiag_states&(TCPF_LISTEN|TCPF_SYN_RECV))) | 609 | if (!(r->tcpdiag_states&(TCPF_LISTEN|TCPF_SYN_RECV))) |
617 | goto skip_listen_ht; | 610 | goto skip_listen_ht; |
@@ -745,13 +738,12 @@ tcpdiag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
745 | if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) | 738 | if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) |
746 | return 0; | 739 | return 0; |
747 | 740 | ||
748 | if (nlh->nlmsg_type != TCPDIAG_GETSOCK | 741 | if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX) |
749 | #ifdef CONFIG_IP_TCPDIAG_DCCP | ||
750 | && nlh->nlmsg_type != DCCPDIAG_GETSOCK | ||
751 | #endif | ||
752 | ) | ||
753 | goto err_inval; | 742 | goto err_inval; |
754 | 743 | ||
744 | if (inet_diag_table[nlh->nlmsg_type] == NULL) | ||
745 | return -ENOENT; | ||
746 | |||
755 | if (NLMSG_LENGTH(sizeof(struct tcpdiagreq)) > skb->len) | 747 | if (NLMSG_LENGTH(sizeof(struct tcpdiagreq)) > skb->len) |
756 | goto err_inval; | 748 | goto err_inval; |
757 | 749 | ||
@@ -803,18 +795,95 @@ static void tcpdiag_rcv(struct sock *sk, int len) | |||
803 | } | 795 | } |
804 | } | 796 | } |
805 | 797 | ||
798 | static void tcp_diag_get_info(struct sock *sk, struct tcpdiagmsg *r, | ||
799 | void *_info) | ||
800 | { | ||
801 | const struct tcp_sock *tp = tcp_sk(sk); | ||
802 | struct tcp_info *info = _info; | ||
803 | |||
804 | r->tcpdiag_rqueue = tp->rcv_nxt - tp->copied_seq; | ||
805 | r->tcpdiag_wqueue = tp->write_seq - tp->snd_una; | ||
806 | if (info != NULL) | ||
807 | tcp_get_info(sk, info); | ||
808 | } | ||
809 | |||
810 | static struct inet_diag_handler tcp_diag_handler = { | ||
811 | .idiag_hashinfo = &tcp_hashinfo, | ||
812 | .idiag_get_info = tcp_diag_get_info, | ||
813 | .idiag_type = TCPDIAG_GETSOCK, | ||
814 | .idiag_info_size = sizeof(struct tcp_info), | ||
815 | }; | ||
816 | |||
817 | static DEFINE_SPINLOCK(inet_diag_register_lock); | ||
818 | |||
819 | int inet_diag_register(const struct inet_diag_handler *h) | ||
820 | { | ||
821 | const __u16 type = h->idiag_type; | ||
822 | int err = -EINVAL; | ||
823 | |||
824 | if (type >= INET_DIAG_GETSOCK_MAX) | ||
825 | goto out; | ||
826 | |||
827 | spin_lock(&inet_diag_register_lock); | ||
828 | err = -EEXIST; | ||
829 | if (inet_diag_table[type] == NULL) { | ||
830 | inet_diag_table[type] = h; | ||
831 | err = 0; | ||
832 | } | ||
833 | spin_unlock(&inet_diag_register_lock); | ||
834 | out: | ||
835 | return err; | ||
836 | } | ||
837 | EXPORT_SYMBOL_GPL(inet_diag_register); | ||
838 | |||
839 | void inet_diag_unregister(const struct inet_diag_handler *h) | ||
840 | { | ||
841 | const __u16 type = h->idiag_type; | ||
842 | |||
843 | if (type >= INET_DIAG_GETSOCK_MAX) | ||
844 | return; | ||
845 | |||
846 | spin_lock(&inet_diag_register_lock); | ||
847 | inet_diag_table[type] = NULL; | ||
848 | spin_unlock(&inet_diag_register_lock); | ||
849 | |||
850 | synchronize_rcu(); | ||
851 | } | ||
852 | EXPORT_SYMBOL_GPL(inet_diag_unregister); | ||
853 | |||
806 | static int __init tcpdiag_init(void) | 854 | static int __init tcpdiag_init(void) |
807 | { | 855 | { |
856 | const int inet_diag_table_size = (INET_DIAG_GETSOCK_MAX * | ||
857 | sizeof(struct inet_diag_handler *)); | ||
858 | int err = -ENOMEM; | ||
859 | |||
860 | inet_diag_table = kmalloc(inet_diag_table_size, GFP_KERNEL); | ||
861 | if (!inet_diag_table) | ||
862 | goto out; | ||
863 | |||
864 | memset(inet_diag_table, 0, inet_diag_table_size); | ||
865 | |||
808 | tcpnl = netlink_kernel_create(NETLINK_TCPDIAG, tcpdiag_rcv, | 866 | tcpnl = netlink_kernel_create(NETLINK_TCPDIAG, tcpdiag_rcv, |
809 | THIS_MODULE); | 867 | THIS_MODULE); |
810 | if (tcpnl == NULL) | 868 | if (tcpnl == NULL) |
811 | return -ENOMEM; | 869 | goto out_free_table; |
812 | return 0; | 870 | |
871 | err = inet_diag_register(&tcp_diag_handler); | ||
872 | if (err) | ||
873 | goto out_sock_release; | ||
874 | out: | ||
875 | return err; | ||
876 | out_sock_release: | ||
877 | sock_release(tcpnl->sk_socket); | ||
878 | out_free_table: | ||
879 | kfree(inet_diag_table); | ||
880 | goto out; | ||
813 | } | 881 | } |
814 | 882 | ||
815 | static void __exit tcpdiag_exit(void) | 883 | static void __exit tcpdiag_exit(void) |
816 | { | 884 | { |
817 | sock_release(tcpnl->sk_socket); | 885 | sock_release(tcpnl->sk_socket); |
886 | kfree(inet_diag_table); | ||
818 | } | 887 | } |
819 | 888 | ||
820 | module_init(tcpdiag_init); | 889 | module_init(tcpdiag_init); |