Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.30-rc4 254 lines 6.1 kB view raw
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. 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 21#include <linux/kernel.h> 22#include <linux/kprobes.h> 23#include <linux/socket.h> 24#include <linux/tcp.h> 25#include <linux/proc_fs.h> 26#include <linux/module.h> 27#include <linux/ktime.h> 28#include <linux/time.h> 29#include <net/net_namespace.h> 30 31#include <net/tcp.h> 32 33MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>"); 34MODULE_DESCRIPTION("TCP cwnd snooper"); 35MODULE_LICENSE("GPL"); 36MODULE_VERSION("1.1"); 37 38static int port __read_mostly = 0; 39MODULE_PARM_DESC(port, "Port to match (0=all)"); 40module_param(port, int, 0); 41 42static int bufsize __read_mostly = 4096; 43MODULE_PARM_DESC(bufsize, "Log buffer size in packets (4096)"); 44module_param(bufsize, int, 0); 45 46static int full __read_mostly; 47MODULE_PARM_DESC(full, "Full log (1=every ack packet received, 0=only cwnd changes)"); 48module_param(full, int, 0); 49 50static const char procname[] = "tcpprobe"; 51 52struct tcp_log { 53 ktime_t tstamp; 54 __be32 saddr, daddr; 55 __be16 sport, dport; 56 u16 length; 57 u32 snd_nxt; 58 u32 snd_una; 59 u32 snd_wnd; 60 u32 snd_cwnd; 61 u32 ssthresh; 62 u32 srtt; 63}; 64 65static struct { 66 spinlock_t lock; 67 wait_queue_head_t wait; 68 ktime_t start; 69 u32 lastcwnd; 70 71 unsigned long head, tail; 72 struct tcp_log *log; 73} tcp_probe; 74 75 76static inline int tcp_probe_used(void) 77{ 78 return (tcp_probe.head - tcp_probe.tail) % bufsize; 79} 80 81static inline int tcp_probe_avail(void) 82{ 83 return bufsize - tcp_probe_used(); 84} 85 86/* 87 * Hook inserted to be called before each receive packet. 88 * Note: arguments must match tcp_rcv_established()! 89 */ 90static int jtcp_rcv_established(struct sock *sk, struct sk_buff *skb, 91 struct tcphdr *th, unsigned len) 92{ 93 const struct tcp_sock *tp = tcp_sk(sk); 94 const struct inet_sock *inet = inet_sk(sk); 95 96 /* Only update if port matches */ 97 if ((port == 0 || ntohs(inet->dport) == port || ntohs(inet->sport) == port) 98 && (full || tp->snd_cwnd != tcp_probe.lastcwnd)) { 99 100 spin_lock(&tcp_probe.lock); 101 /* If log fills, just silently drop */ 102 if (tcp_probe_avail() > 1) { 103 struct tcp_log *p = tcp_probe.log + tcp_probe.head; 104 105 p->tstamp = ktime_get(); 106 p->saddr = inet->saddr; 107 p->sport = inet->sport; 108 p->daddr = inet->daddr; 109 p->dport = inet->dport; 110 p->length = skb->len; 111 p->snd_nxt = tp->snd_nxt; 112 p->snd_una = tp->snd_una; 113 p->snd_cwnd = tp->snd_cwnd; 114 p->snd_wnd = tp->snd_wnd; 115 p->ssthresh = tcp_current_ssthresh(sk); 116 p->srtt = tp->srtt >> 3; 117 118 tcp_probe.head = (tcp_probe.head + 1) % bufsize; 119 } 120 tcp_probe.lastcwnd = tp->snd_cwnd; 121 spin_unlock(&tcp_probe.lock); 122 123 wake_up(&tcp_probe.wait); 124 } 125 126 jprobe_return(); 127 return 0; 128} 129 130static struct jprobe tcp_jprobe = { 131 .kp = { 132 .symbol_name = "tcp_rcv_established", 133 }, 134 .entry = jtcp_rcv_established, 135}; 136 137static int tcpprobe_open(struct inode * inode, struct file * file) 138{ 139 /* Reset (empty) log */ 140 spin_lock_bh(&tcp_probe.lock); 141 tcp_probe.head = tcp_probe.tail = 0; 142 tcp_probe.start = ktime_get(); 143 spin_unlock_bh(&tcp_probe.lock); 144 145 return 0; 146} 147 148static int tcpprobe_sprint(char *tbuf, int n) 149{ 150 const struct tcp_log *p 151 = tcp_probe.log + tcp_probe.tail % bufsize; 152 struct timespec tv 153 = ktime_to_timespec(ktime_sub(p->tstamp, tcp_probe.start)); 154 155 return snprintf(tbuf, n, 156 "%lu.%09lu %pI4:%u %pI4:%u %d %#x %#x %u %u %u %u\n", 157 (unsigned long) tv.tv_sec, 158 (unsigned long) tv.tv_nsec, 159 &p->saddr, ntohs(p->sport), 160 &p->daddr, ntohs(p->dport), 161 p->length, p->snd_nxt, p->snd_una, 162 p->snd_cwnd, p->ssthresh, p->snd_wnd, p->srtt); 163} 164 165static ssize_t tcpprobe_read(struct file *file, char __user *buf, 166 size_t len, loff_t *ppos) 167{ 168 int error = 0; 169 size_t cnt = 0; 170 171 if (!buf) 172 return -EINVAL; 173 174 while (cnt < len) { 175 char tbuf[128]; 176 int width; 177 178 /* Wait for data in buffer */ 179 error = wait_event_interruptible(tcp_probe.wait, 180 tcp_probe_used() > 0); 181 if (error) 182 break; 183 184 spin_lock_bh(&tcp_probe.lock); 185 if (tcp_probe.head == tcp_probe.tail) { 186 /* multiple readers race? */ 187 spin_unlock_bh(&tcp_probe.lock); 188 continue; 189 } 190 191 width = tcpprobe_sprint(tbuf, sizeof(tbuf)); 192 193 if (cnt + width < len) 194 tcp_probe.tail = (tcp_probe.tail + 1) % bufsize; 195 196 spin_unlock_bh(&tcp_probe.lock); 197 198 /* if record greater than space available 199 return partial buffer (so far) */ 200 if (cnt + width >= len) 201 break; 202 203 if (copy_to_user(buf + cnt, tbuf, width)) 204 return -EFAULT; 205 cnt += width; 206 } 207 208 return cnt == 0 ? error : cnt; 209} 210 211static const struct file_operations tcpprobe_fops = { 212 .owner = THIS_MODULE, 213 .open = tcpprobe_open, 214 .read = tcpprobe_read, 215}; 216 217static __init int tcpprobe_init(void) 218{ 219 int ret = -ENOMEM; 220 221 init_waitqueue_head(&tcp_probe.wait); 222 spin_lock_init(&tcp_probe.lock); 223 224 if (bufsize < 0) 225 return -EINVAL; 226 227 tcp_probe.log = kcalloc(bufsize, sizeof(struct tcp_log), GFP_KERNEL); 228 if (!tcp_probe.log) 229 goto err0; 230 231 if (!proc_net_fops_create(&init_net, procname, S_IRUSR, &tcpprobe_fops)) 232 goto err0; 233 234 ret = register_jprobe(&tcp_jprobe); 235 if (ret) 236 goto err1; 237 238 pr_info("TCP probe registered (port=%d)\n", port); 239 return 0; 240 err1: 241 proc_net_remove(&init_net, procname); 242 err0: 243 kfree(tcp_probe.log); 244 return ret; 245} 246module_init(tcpprobe_init); 247 248static __exit void tcpprobe_exit(void) 249{ 250 proc_net_remove(&init_net, procname); 251 unregister_jprobe(&tcp_jprobe); 252 kfree(tcp_probe.log); 253} 254module_exit(tcpprobe_exit);