net: tcp_probe: also include rcv_wnd next to snd_wnd
[deliverable/linux.git] / net / ipv4 / tcp_probe.c
CommitLineData
a42e9d6c
SH
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
662ad4f8 9 * the Free Software Foundation; either version 2 of the License.
a42e9d6c
SH
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
afd46503
JP
21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22
a42e9d6c
SH
23#include <linux/kernel.h>
24#include <linux/kprobes.h>
25#include <linux/socket.h>
26#include <linux/tcp.h>
5a0e3ad6 27#include <linux/slab.h>
a42e9d6c
SH
28#include <linux/proc_fs.h>
29#include <linux/module.h>
85795d64
SH
30#include <linux/ktime.h>
31#include <linux/time.h>
457c4cbc 32#include <net/net_namespace.h>
a42e9d6c
SH
33
34#include <net/tcp.h>
35
65ebe634 36MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
a42e9d6c
SH
37MODULE_DESCRIPTION("TCP cwnd snooper");
38MODULE_LICENSE("GPL");
662ad4f8 39MODULE_VERSION("1.1");
a42e9d6c 40
85795d64 41static int port __read_mostly = 0;
a42e9d6c
SH
42MODULE_PARM_DESC(port, "Port to match (0=all)");
43module_param(port, int, 0);
44
f81074f8 45static unsigned int bufsize __read_mostly = 4096;
662ad4f8 46MODULE_PARM_DESC(bufsize, "Log buffer size in packets (4096)");
f81074f8 47module_param(bufsize, uint, 0);
a42e9d6c 48
85795d64
SH
49static int full __read_mostly;
50MODULE_PARM_DESC(full, "Full log (1=every ack packet received, 0=only cwnd changes)");
51module_param(full, int, 0);
52
a42e9d6c
SH
53static const char procname[] = "tcpprobe";
54
662ad4f8
SH
55struct tcp_log {
56 ktime_t tstamp;
57 __be32 saddr, daddr;
58 __be16 sport, dport;
59 u16 length;
60 u32 snd_nxt;
61 u32 snd_una;
62 u32 snd_wnd;
b4c1c1d0 63 u32 rcv_wnd;
662ad4f8
SH
64 u32 snd_cwnd;
65 u32 ssthresh;
66 u32 srtt;
67};
68
69static struct {
85795d64 70 spinlock_t lock;
a42e9d6c 71 wait_queue_head_t wait;
85795d64
SH
72 ktime_t start;
73 u32 lastcwnd;
a42e9d6c 74
662ad4f8
SH
75 unsigned long head, tail;
76 struct tcp_log *log;
77} tcp_probe;
78
14a49e1f 79
662ad4f8 80static inline int tcp_probe_used(void)
a42e9d6c 81{
f81074f8 82 return (tcp_probe.head - tcp_probe.tail) & (bufsize - 1);
662ad4f8
SH
83}
84
85static inline int tcp_probe_avail(void)
86{
f81074f8 87 return bufsize - tcp_probe_used() - 1;
14a49e1f 88}
a42e9d6c 89
85795d64
SH
90/*
91 * Hook inserted to be called before each receive packet.
92 * Note: arguments must match tcp_rcv_established()!
93 */
94static int jtcp_rcv_established(struct sock *sk, struct sk_buff *skb,
95c96174 95 struct tcphdr *th, unsigned int len)
a42e9d6c
SH
96{
97 const struct tcp_sock *tp = tcp_sk(sk);
98 const struct inet_sock *inet = inet_sk(sk);
99
85795d64 100 /* Only update if port matches */
c720c7e8 101 if ((port == 0 || ntohs(inet->inet_dport) == port ||
9d4fb27d
JP
102 ntohs(inet->inet_sport) == port) &&
103 (full || tp->snd_cwnd != tcp_probe.lastcwnd)) {
662ad4f8
SH
104
105 spin_lock(&tcp_probe.lock);
106 /* If log fills, just silently drop */
107 if (tcp_probe_avail() > 1) {
108 struct tcp_log *p = tcp_probe.log + tcp_probe.head;
109
110 p->tstamp = ktime_get();
c720c7e8
ED
111 p->saddr = inet->inet_saddr;
112 p->sport = inet->inet_sport;
113 p->daddr = inet->inet_daddr;
114 p->dport = inet->inet_dport;
662ad4f8
SH
115 p->length = skb->len;
116 p->snd_nxt = tp->snd_nxt;
117 p->snd_una = tp->snd_una;
118 p->snd_cwnd = tp->snd_cwnd;
119 p->snd_wnd = tp->snd_wnd;
b4c1c1d0 120 p->rcv_wnd = tp->rcv_wnd;
b3b0b681 121 p->ssthresh = tcp_current_ssthresh(sk);
662ad4f8
SH
122 p->srtt = tp->srtt >> 3;
123
f81074f8 124 tcp_probe.head = (tcp_probe.head + 1) & (bufsize - 1);
662ad4f8
SH
125 }
126 tcp_probe.lastcwnd = tp->snd_cwnd;
127 spin_unlock(&tcp_probe.lock);
128
129 wake_up(&tcp_probe.wait);
a42e9d6c
SH
130 }
131
132 jprobe_return();
133 return 0;
134}
135
662ad4f8 136static struct jprobe tcp_jprobe = {
3a872d89 137 .kp = {
85795d64 138 .symbol_name = "tcp_rcv_established",
3a872d89 139 },
9e367d85 140 .entry = jtcp_rcv_established,
a42e9d6c
SH
141};
142
5e73ea1a 143static int tcpprobe_open(struct inode *inode, struct file *file)
a42e9d6c 144{
662ad4f8
SH
145 /* Reset (empty) log */
146 spin_lock_bh(&tcp_probe.lock);
147 tcp_probe.head = tcp_probe.tail = 0;
148 tcp_probe.start = ktime_get();
149 spin_unlock_bh(&tcp_probe.lock);
150
a42e9d6c
SH
151 return 0;
152}
153
662ad4f8
SH
154static int tcpprobe_sprint(char *tbuf, int n)
155{
156 const struct tcp_log *p
f81074f8 157 = tcp_probe.log + tcp_probe.tail;
662ad4f8
SH
158 struct timespec tv
159 = ktime_to_timespec(ktime_sub(p->tstamp, tcp_probe.start));
160
dda0b386 161 return scnprintf(tbuf, n,
b4c1c1d0 162 "%lu.%09lu %pI4:%u %pI4:%u %d %#x %#x %u %u %u %u %u\n",
662ad4f8
SH
163 (unsigned long) tv.tv_sec,
164 (unsigned long) tv.tv_nsec,
673d57e7
HH
165 &p->saddr, ntohs(p->sport),
166 &p->daddr, ntohs(p->dport),
662ad4f8 167 p->length, p->snd_nxt, p->snd_una,
b4c1c1d0 168 p->snd_cwnd, p->ssthresh, p->snd_wnd, p->srtt, p->rcv_wnd);
662ad4f8
SH
169}
170
a42e9d6c
SH
171static ssize_t tcpprobe_read(struct file *file, char __user *buf,
172 size_t len, loff_t *ppos)
173{
a2025b8b
RK
174 int error = 0;
175 size_t cnt = 0;
a42e9d6c 176
a2025b8b 177 if (!buf)
a42e9d6c
SH
178 return -EINVAL;
179
662ad4f8 180 while (cnt < len) {
dda0b386 181 char tbuf[164];
662ad4f8
SH
182 int width;
183
184 /* Wait for data in buffer */
185 error = wait_event_interruptible(tcp_probe.wait,
186 tcp_probe_used() > 0);
187 if (error)
188 break;
a42e9d6c 189
662ad4f8
SH
190 spin_lock_bh(&tcp_probe.lock);
191 if (tcp_probe.head == tcp_probe.tail) {
192 /* multiple readers race? */
193 spin_unlock_bh(&tcp_probe.lock);
194 continue;
195 }
a42e9d6c 196
662ad4f8 197 width = tcpprobe_sprint(tbuf, sizeof(tbuf));
a42e9d6c 198
8d390efd 199 if (cnt + width < len)
f81074f8 200 tcp_probe.tail = (tcp_probe.tail + 1) & (bufsize - 1);
a42e9d6c 201
662ad4f8
SH
202 spin_unlock_bh(&tcp_probe.lock);
203
204 /* if record greater than space available
205 return partial buffer (so far) */
8d390efd 206 if (cnt + width >= len)
662ad4f8
SH
207 break;
208
8d390efd
TQ
209 if (copy_to_user(buf + cnt, tbuf, width))
210 return -EFAULT;
662ad4f8
SH
211 cnt += width;
212 }
a42e9d6c 213
662ad4f8 214 return cnt == 0 ? error : cnt;
a42e9d6c
SH
215}
216
9a32144e 217static const struct file_operations tcpprobe_fops = {
a42e9d6c
SH
218 .owner = THIS_MODULE,
219 .open = tcpprobe_open,
220 .read = tcpprobe_read,
6038f373 221 .llseek = noop_llseek,
a42e9d6c
SH
222};
223
224static __init int tcpprobe_init(void)
225{
226 int ret = -ENOMEM;
227
662ad4f8
SH
228 init_waitqueue_head(&tcp_probe.wait);
229 spin_lock_init(&tcp_probe.lock);
230
f81074f8 231 if (bufsize == 0)
662ad4f8
SH
232 return -EINVAL;
233
f81074f8 234 bufsize = roundup_pow_of_two(bufsize);
3d8ea1fd 235 tcp_probe.log = kcalloc(bufsize, sizeof(struct tcp_log), GFP_KERNEL);
662ad4f8
SH
236 if (!tcp_probe.log)
237 goto err0;
a42e9d6c 238
d4beaa66 239 if (!proc_create(procname, S_IRUSR, init_net.proc_net, &tcpprobe_fops))
a42e9d6c
SH
240 goto err0;
241
662ad4f8 242 ret = register_jprobe(&tcp_jprobe);
a42e9d6c
SH
243 if (ret)
244 goto err1;
245
afd46503 246 pr_info("probe registered (port=%d) bufsize=%u\n", port, bufsize);
a42e9d6c
SH
247 return 0;
248 err1:
ece31ffd 249 remove_proc_entry(procname, init_net.proc_net);
a42e9d6c 250 err0:
662ad4f8 251 kfree(tcp_probe.log);
a42e9d6c
SH
252 return ret;
253}
254module_init(tcpprobe_init);
255
256static __exit void tcpprobe_exit(void)
257{
ece31ffd 258 remove_proc_entry(procname, init_net.proc_net);
662ad4f8
SH
259 unregister_jprobe(&tcp_jprobe);
260 kfree(tcp_probe.log);
a42e9d6c
SH
261}
262module_exit(tcpprobe_exit);
This page took 0.855619 seconds and 5 git commands to generate.