aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/tcp_diag.h19
-rw-r--r--net/dccp/Kconfig5
-rw-r--r--net/dccp/Makefile4
-rw-r--r--net/dccp/diag.c47
-rw-r--r--net/ipv4/Kconfig3
-rw-r--r--net/ipv4/tcp_diag.c153
6 files changed, 186 insertions, 45 deletions
diff --git a/include/linux/tcp_diag.h b/include/linux/tcp_diag.h
index 190494ebcfb8..910c34ba19c0 100644
--- a/include/linux/tcp_diag.h
+++ b/include/linux/tcp_diag.h
@@ -5,6 +5,8 @@
5#define TCPDIAG_GETSOCK 18 5#define TCPDIAG_GETSOCK 18
6#define DCCPDIAG_GETSOCK 19 6#define DCCPDIAG_GETSOCK 19
7 7
8#define INET_DIAG_GETSOCK_MAX 24
9
8/* Socket identity */ 10/* Socket identity */
9struct tcpdiag_sockid 11struct tcpdiag_sockid
10{ 12{
@@ -125,4 +127,21 @@ struct tcpvegas_info {
125 __u32 tcpv_minrtt; 127 __u32 tcpv_minrtt;
126}; 128};
127 129
130#ifdef __KERNEL__
131struct sock;
132struct inet_hashinfo;
133
134struct inet_diag_handler {
135 struct inet_hashinfo *idiag_hashinfo;
136 void (*idiag_get_info)(struct sock *sk,
137 struct tcpdiagmsg *r,
138 void *info);
139 __u16 idiag_info_size;
140 __u16 idiag_type;
141};
142
143extern int inet_diag_register(const struct inet_diag_handler *handler);
144extern void inet_diag_unregister(const struct inet_diag_handler *handler);
145#endif /* __KERNEL__ */
146
128#endif /* _TCP_DIAG_H_ */ 147#endif /* _TCP_DIAG_H_ */
diff --git a/net/dccp/Kconfig b/net/dccp/Kconfig
index 90460bc629b3..ff5b5459b97a 100644
--- a/net/dccp/Kconfig
+++ b/net/dccp/Kconfig
@@ -19,6 +19,11 @@ config IP_DCCP
19 19
20 If in doubt, say N. 20 If in doubt, say N.
21 21
22config IP_DCCP_DIAG
23 depends on IP_DCCP && IP_TCPDIAG
24 def_tristate y if (IP_DCCP = y && IP_TCPDIAG = y)
25 def_tristate m
26
22source "net/dccp/ccids/Kconfig" 27source "net/dccp/ccids/Kconfig"
23 28
24endmenu 29endmenu
diff --git a/net/dccp/Makefile b/net/dccp/Makefile
index 25a50bdbf1bb..5741fffc436f 100644
--- a/net/dccp/Makefile
+++ b/net/dccp/Makefile
@@ -3,4 +3,8 @@ obj-$(CONFIG_IP_DCCP) += dccp.o
3dccp-y := ccid.o input.o ipv4.o minisocks.o options.o output.o proto.o \ 3dccp-y := ccid.o input.o ipv4.o minisocks.o options.o output.o proto.o \
4 timer.o packet_history.o 4 timer.o packet_history.o
5 5
6obj-$(CONFIG_IP_DCCP_DIAG) += dccp_diag.o
7
6obj-y += ccids/ 8obj-y += ccids/
9
10dccp_diag-y := diag.o
diff --git a/net/dccp/diag.c b/net/dccp/diag.c
new file mode 100644
index 000000000000..4d9037c56ddc
--- /dev/null
+++ b/net/dccp/diag.c
@@ -0,0 +1,47 @@
1/*
2 * net/dccp/diag.c
3 *
4 * An implementation of the DCCP protocol
5 * Arnaldo Carvalho de Melo <acme@mandriva.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/config.h>
13
14#include <linux/module.h>
15#include <linux/tcp_diag.h>
16
17#include "dccp.h"
18
19static void dccp_diag_get_info(struct sock *sk, struct tcpdiagmsg *r,
20 void *_info)
21{
22 r->tcpdiag_rqueue = r->tcpdiag_wqueue = 0;
23}
24
25static struct inet_diag_handler dccp_diag_handler = {
26 .idiag_hashinfo = &dccp_hashinfo,
27 .idiag_get_info = dccp_diag_get_info,
28 .idiag_type = DCCPDIAG_GETSOCK,
29 .idiag_info_size = 0,
30};
31
32static int __init dccp_diag_init(void)
33{
34 return inet_diag_register(&dccp_diag_handler);
35}
36
37static void __exit dccp_diag_fini(void)
38{
39 inet_diag_unregister(&dccp_diag_handler);
40}
41
42module_init(dccp_diag_init);
43module_exit(dccp_diag_fini);
44
45MODULE_LICENSE("GPL");
46MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>");
47MODULE_DESCRIPTION("DCCP inet_diag handler");
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
426config IP_TCPDIAG_DCCP
427 def_bool (IP_TCPDIAG=y && IP_DCCP=y) || (IP_TCPDIAG=m && IP_DCCP)
428
429config TCP_CONG_ADVANCED 426config 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
37static const struct inet_diag_handler **inet_diag_table;
38
37struct tcpdiag_entry 39struct 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
798static 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
810static 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
817static DEFINE_SPINLOCK(inet_diag_register_lock);
818
819int 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);
834out:
835 return err;
836}
837EXPORT_SYMBOL_GPL(inet_diag_register);
838
839void 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}
852EXPORT_SYMBOL_GPL(inet_diag_unregister);
853
806static int __init tcpdiag_init(void) 854static 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;
874out:
875 return err;
876out_sock_release:
877 sock_release(tcpnl->sk_socket);
878out_free_table:
879 kfree(inet_diag_table);
880 goto out;
813} 881}
814 882
815static void __exit tcpdiag_exit(void) 883static 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
820module_init(tcpdiag_init); 889module_init(tcpdiag_init);