aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-06-05 20:30:32 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-06-18 00:29:31 -0400
commita42e9d6ce89cfd19aee9f990b7231ce697f0d00f (patch)
tree3afea026bdf3698fd81509925d8cf7d5f1e95fbd
parent72dc5b9225c53310c010b68a70ea97c8c8e24bdf (diff)
[TCP]: TCP Probe congestion window tracing
This adds a new module for tracking TCP state variables non-intrusively using kprobes. It has a simple /proc interface that outputs one line for each packet received. A sample usage is to collect congestion window and ssthresh over time graphs. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/Kconfig15
-rw-r--r--net/ipv4/Makefile1
-rw-r--r--net/ipv4/tcp_probe.c179
3 files changed, 195 insertions, 0 deletions
diff --git a/net/Kconfig b/net/Kconfig
index 4193cdcd3ae7..ff0db804eed8 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -215,6 +215,21 @@ config NET_PKTGEN
215 To compile this code as a module, choose M here: the 215 To compile this code as a module, choose M here: the
216 module will be called pktgen. 216 module will be called pktgen.
217 217
218config NET_TCPPROBE
219 tristate "TCP connection probing"
220 depends on INET && EXPERIMENTAL && PROC_FS && KPROBES
221 ---help---
222 This module allows for capturing the changes to TCP connection
223 state in response to incoming patckets. It is used for debugging
224 TCP congestion avoidance modules. If you don't understand
225 what was just said, you don't need it: say N.
226
227 Documentation on how to use the packet generator can be found
228 at http://linux-net.osdl.org/index.php/TcpProbe
229
230 To compile this code as a module, choose M here: the
231 module will be called tcp_probe.
232
218endmenu 233endmenu
219 234
220endmenu 235endmenu
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index f30faefc2672..38b8039bdd55 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_IP_VS) += ipvs/
36obj-$(CONFIG_INET_DIAG) += inet_diag.o 36obj-$(CONFIG_INET_DIAG) += inet_diag.o
37obj-$(CONFIG_IP_ROUTE_MULTIPATH_CACHED) += multipath.o 37obj-$(CONFIG_IP_ROUTE_MULTIPATH_CACHED) += multipath.o
38obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o 38obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o
39obj-$(CONFIG_NET_TCPPROBE) += tcp_probe.o
39obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o 40obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
40obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o 41obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o
41obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o 42obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o
diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c
new file mode 100644
index 000000000000..be94d526c7cb
--- /dev/null
+++ b/net/ipv4/tcp_probe.c
@@ -0,0 +1,179 @@
1/*
2 * tcpprobe - Observe the TCP flow with kprobes.
3 *
4 * The idea for this came from Werner Almesberger's umlsim
5 * Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <linux/kernel.h>
23#include <linux/kprobes.h>
24#include <linux/socket.h>
25#include <linux/tcp.h>
26#include <linux/proc_fs.h>
27#include <linux/module.h>
28#include <linux/kfifo.h>
29#include <linux/vmalloc.h>
30
31#include <net/tcp.h>
32
33MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
34MODULE_DESCRIPTION("TCP cwnd snooper");
35MODULE_LICENSE("GPL");
36
37static int port = 0;
38MODULE_PARM_DESC(port, "Port to match (0=all)");
39module_param(port, int, 0);
40
41static int bufsize = 64*1024;
42MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)");
43module_param(bufsize, int, 0);
44
45static const char procname[] = "tcpprobe";
46
47struct {
48 struct kfifo *fifo;
49 spinlock_t lock;
50 wait_queue_head_t wait;
51 struct timeval tstart;
52} tcpw;
53
54static void printl(const char *fmt, ...)
55{
56 va_list args;
57 int len;
58 struct timeval now;
59 char tbuf[256];
60
61 va_start(args, fmt);
62 do_gettimeofday(&now);
63
64 now.tv_sec -= tcpw.tstart.tv_sec;
65 now.tv_usec -= tcpw.tstart.tv_usec;
66 if (now.tv_usec < 0) {
67 --now.tv_sec;
68 now.tv_usec += 1000000;
69 }
70
71 len = sprintf(tbuf, "%lu.%06lu ", now.tv_sec, now.tv_usec);
72 len += vscnprintf(tbuf+len, sizeof(tbuf)-len, fmt, args);
73 va_end(args);
74
75 kfifo_put(tcpw.fifo, tbuf, len);
76 wake_up(&tcpw.wait);
77}
78
79static int jtcp_sendmsg(struct kiocb *iocb, struct sock *sk,
80 struct msghdr *msg, size_t size)
81{
82 const struct tcp_sock *tp = tcp_sk(sk);
83 const struct inet_sock *inet = inet_sk(sk);
84
85 if (port == 0 || ntohs(inet->dport) == port ||
86 ntohs(inet->sport) == port) {
87 printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %#x %#x %u %u %u\n",
88 NIPQUAD(inet->saddr), ntohs(inet->sport),
89 NIPQUAD(inet->daddr), ntohs(inet->dport),
90 size, tp->snd_nxt, tp->snd_una,
91 tp->snd_cwnd, tcp_current_ssthresh(sk),
92 tp->snd_wnd);
93 }
94
95 jprobe_return();
96 return 0;
97}
98
99static struct jprobe tcp_send_probe = {
100 .kp = { .addr = (kprobe_opcode_t *) &tcp_sendmsg, },
101 .entry = (kprobe_opcode_t *) &jtcp_sendmsg,
102};
103
104
105static int tcpprobe_open(struct inode * inode, struct file * file)
106{
107 kfifo_reset(tcpw.fifo);
108 do_gettimeofday(&tcpw.tstart);
109 return 0;
110}
111
112static ssize_t tcpprobe_read(struct file *file, char __user *buf,
113 size_t len, loff_t *ppos)
114{
115 int error = 0, cnt;
116 unsigned char *tbuf;
117
118 if (!buf || len < 0)
119 return -EINVAL;
120
121 if (len == 0)
122 return 0;
123
124 tbuf = vmalloc(len);
125 if (!tbuf)
126 return -ENOMEM;
127
128 error = wait_event_interruptible(tcpw.wait,
129 __kfifo_len(tcpw.fifo) != 0);
130 if (error)
131 return error;
132
133 cnt = kfifo_get(tcpw.fifo, tbuf, len);
134 error = copy_to_user(buf, tbuf, cnt);
135
136 vfree(tbuf);
137
138 return error ? error : cnt;
139}
140
141static struct file_operations tcpprobe_fops = {
142 .owner = THIS_MODULE,
143 .open = tcpprobe_open,
144 .read = tcpprobe_read,
145};
146
147static __init int tcpprobe_init(void)
148{
149 int ret = -ENOMEM;
150
151 init_waitqueue_head(&tcpw.wait);
152 spin_lock_init(&tcpw.lock);
153 tcpw.fifo = kfifo_alloc(bufsize, GFP_KERNEL, &tcpw.lock);
154
155 if (!proc_net_fops_create(procname, S_IRUSR, &tcpprobe_fops))
156 goto err0;
157
158 ret = register_jprobe(&tcp_send_probe);
159 if (ret)
160 goto err1;
161
162 pr_info("TCP watch registered (port=%d)\n", port);
163 return 0;
164 err1:
165 proc_net_remove(procname);
166 err0:
167 kfifo_free(tcpw.fifo);
168 return ret;
169}
170module_init(tcpprobe_init);
171
172static __exit void tcpprobe_exit(void)
173{
174 kfifo_free(tcpw.fifo);
175 proc_net_remove(procname);
176 unregister_jprobe(&tcp_send_probe);
177
178}
179module_exit(tcpprobe_exit);