1/* 2 * sctp_probe - Observe the SCTP 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 * Modified for SCTP from Stephen Hemminger's code 8 * Copyright (C) 2010, Wei Yongjun <yjwei@cn.fujitsu.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 */ 24 25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 26 27#include <linux/kernel.h> 28#include <linux/kprobes.h> 29#include <linux/socket.h> 30#include <linux/sctp.h> 31#include <linux/proc_fs.h> 32#include <linux/vmalloc.h> 33#include <linux/module.h> 34#include <linux/kfifo.h> 35#include <linux/time.h> 36#include <net/net_namespace.h> 37 38#include <net/sctp/sctp.h> 39#include <net/sctp/sm.h> 40 41MODULE_SOFTDEP("pre: sctp"); 42MODULE_AUTHOR("Wei Yongjun <yjwei@cn.fujitsu.com>"); 43MODULE_DESCRIPTION("SCTP snooper"); 44MODULE_LICENSE("GPL"); 45 46static int port __read_mostly = 0; 47MODULE_PARM_DESC(port, "Port to match (0=all)"); 48module_param(port, int, 0); 49 50static unsigned int fwmark __read_mostly = 0; 51MODULE_PARM_DESC(fwmark, "skb mark to match (0=no mark)"); 52module_param(fwmark, uint, 0); 53 54static int bufsize __read_mostly = 64 * 1024; 55MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)"); 56module_param(bufsize, int, 0); 57 58static int full __read_mostly = 1; 59MODULE_PARM_DESC(full, "Full log (1=every ack packet received, 0=only cwnd changes)"); 60module_param(full, int, 0); 61 62static const char procname[] = "sctpprobe"; 63 64static struct { 65 struct kfifo fifo; 66 spinlock_t lock; 67 wait_queue_head_t wait; 68 struct timespec tstart; 69} sctpw; 70 71static __printf(1, 2) void printl(const char *fmt, ...) 72{ 73 va_list args; 74 int len; 75 char tbuf[256]; 76 77 va_start(args, fmt); 78 len = vscnprintf(tbuf, sizeof(tbuf), fmt, args); 79 va_end(args); 80 81 kfifo_in_locked(&sctpw.fifo, tbuf, len, &sctpw.lock); 82 wake_up(&sctpw.wait); 83} 84 85static int sctpprobe_open(struct inode *inode, struct file *file) 86{ 87 kfifo_reset(&sctpw.fifo); 88 getnstimeofday(&sctpw.tstart); 89 90 return 0; 91} 92 93static ssize_t sctpprobe_read(struct file *file, char __user *buf, 94 size_t len, loff_t *ppos) 95{ 96 int error = 0, cnt = 0; 97 unsigned char *tbuf; 98 99 if (!buf) 100 return -EINVAL; 101 102 if (len == 0) 103 return 0; 104 105 tbuf = vmalloc(len); 106 if (!tbuf) 107 return -ENOMEM; 108 109 error = wait_event_interruptible(sctpw.wait, 110 kfifo_len(&sctpw.fifo) != 0); 111 if (error) 112 goto out_free; 113 114 cnt = kfifo_out_locked(&sctpw.fifo, tbuf, len, &sctpw.lock); 115 error = copy_to_user(buf, tbuf, cnt) ? -EFAULT : 0; 116 117out_free: 118 vfree(tbuf); 119 120 return error ? error : cnt; 121} 122 123static const struct file_operations sctpprobe_fops = { 124 .owner = THIS_MODULE, 125 .open = sctpprobe_open, 126 .read = sctpprobe_read, 127 .llseek = noop_llseek, 128}; 129 130static sctp_disposition_t jsctp_sf_eat_sack(struct net *net, 131 const struct sctp_endpoint *ep, 132 const struct sctp_association *asoc, 133 const sctp_subtype_t type, 134 void *arg, 135 sctp_cmd_seq_t *commands) 136{ 137 struct sctp_chunk *chunk = arg; 138 struct sk_buff *skb = chunk->skb; 139 struct sctp_transport *sp; 140 static __u32 lcwnd = 0; 141 struct timespec now; 142 143 sp = asoc->peer.primary_path; 144 145 if (((port == 0 && fwmark == 0) || 146 asoc->peer.port == port || 147 ep->base.bind_addr.port == port || 148 (fwmark > 0 && skb->mark == fwmark)) && 149 (full || sp->cwnd != lcwnd)) { 150 lcwnd = sp->cwnd; 151 152 getnstimeofday(&now); 153 now = timespec_sub(now, sctpw.tstart); 154 155 printl("%lu.%06lu ", (unsigned long) now.tv_sec, 156 (unsigned long) now.tv_nsec / NSEC_PER_USEC); 157 158 printl("%p %5d %5d %5d %8d %5d ", asoc, 159 ep->base.bind_addr.port, asoc->peer.port, 160 asoc->pathmtu, asoc->peer.rwnd, asoc->unack_data); 161 162 list_for_each_entry(sp, &asoc->peer.transport_addr_list, 163 transports) { 164 if (sp == asoc->peer.primary_path) 165 printl("*"); 166 167 printl("%pISc %2u %8u %8u %8u %8u %8u ", 168 &sp->ipaddr, sp->state, sp->cwnd, sp->ssthresh, 169 sp->flight_size, sp->partial_bytes_acked, 170 sp->pathmtu); 171 } 172 printl("\n"); 173 } 174 175 jprobe_return(); 176 return 0; 177} 178 179static struct jprobe sctp_recv_probe = { 180 .kp = { 181 .symbol_name = "sctp_sf_eat_sack_6_2", 182 }, 183 .entry = jsctp_sf_eat_sack, 184}; 185 186static __init int sctp_setup_jprobe(void) 187{ 188 int ret = register_jprobe(&sctp_recv_probe); 189 190 if (ret) { 191 if (request_module("sctp")) 192 goto out; 193 ret = register_jprobe(&sctp_recv_probe); 194 } 195 196out: 197 return ret; 198} 199 200static __init int sctpprobe_init(void) 201{ 202 int ret = -ENOMEM; 203 204 /* Warning: if the function signature of sctp_sf_eat_sack_6_2, 205 * has been changed, you also have to change the signature of 206 * jsctp_sf_eat_sack, otherwise you end up right here! 207 */ 208 BUILD_BUG_ON(__same_type(sctp_sf_eat_sack_6_2, 209 jsctp_sf_eat_sack) == 0); 210 211 init_waitqueue_head(&sctpw.wait); 212 spin_lock_init(&sctpw.lock); 213 if (kfifo_alloc(&sctpw.fifo, bufsize, GFP_KERNEL)) 214 return ret; 215 216 if (!proc_create(procname, S_IRUSR, init_net.proc_net, 217 &sctpprobe_fops)) 218 goto free_kfifo; 219 220 ret = sctp_setup_jprobe(); 221 if (ret) 222 goto remove_proc; 223 224 pr_info("probe registered (port=%d/fwmark=%u) bufsize=%u\n", 225 port, fwmark, bufsize); 226 return 0; 227 228remove_proc: 229 remove_proc_entry(procname, init_net.proc_net); 230free_kfifo: 231 kfifo_free(&sctpw.fifo); 232 return ret; 233} 234 235static __exit void sctpprobe_exit(void) 236{ 237 kfifo_free(&sctpw.fifo); 238 remove_proc_entry(procname, init_net.proc_net); 239 unregister_jprobe(&sctp_recv_probe); 240} 241 242module_init(sctpprobe_init); 243module_exit(sctpprobe_exit); 244