aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Chapman <jchapman@katalix.com>2010-04-02 02:19:33 -0400
committerDavid S. Miller <davem@davemloft.net>2010-04-03 17:56:07 -0400
commit0ad6614048cf722e4d27909665b4846805357f1b (patch)
tree182349ddf6e2b8e8478a5451cd0ed9684c14f0e8
parentd9e31d17ceba5f0736f5a34bbc236239cd42b420 (diff)
l2tp: Add debugfs files for dumping l2tp debug info
The existing pppol2tp driver exports debug info to /proc/net/pppol2tp. Rather than adding info to that file for the new functionality added in this patch series, we add new files in debugfs, leaving the old /proc file for backwards compatibility (L2TPv2 only). Currently only one file is provided: l2tp/tunnels, which lists internal debug info for all l2tp tunnels and sessions. More files may be added later. The info is for debug and problem analysis only - userspace apps should use netlink to obtain status about l2tp tunnels and sessions. Although debugfs does not support net namespaces, the tunnels and sessions dumped in l2tp/tunnels are only those in the net namespace of the process reading the file. Signed-off-by: James Chapman <jchapman@katalix.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/l2tp/Kconfig13
-rw-r--r--net/l2tp/Makefile1
-rw-r--r--net/l2tp/l2tp_core.h8
-rw-r--r--net/l2tp/l2tp_debugfs.c341
-rw-r--r--net/l2tp/l2tp_eth.c14
-rw-r--r--net/l2tp/l2tp_ppp.c17
6 files changed, 392 insertions, 2 deletions
diff --git a/net/l2tp/Kconfig b/net/l2tp/Kconfig
index a292270c0abe..4b1e71751e10 100644
--- a/net/l2tp/Kconfig
+++ b/net/l2tp/Kconfig
@@ -31,6 +31,19 @@ menuconfig L2TP
31 If you don't need L2TP, say N. To compile all L2TP code as 31 If you don't need L2TP, say N. To compile all L2TP code as
32 modules, choose M here. 32 modules, choose M here.
33 33
34config L2TP_DEBUGFS
35 tristate "L2TP debugfs support"
36 depends on L2TP && DEBUG_FS
37 help
38 Support for l2tp directory in debugfs filesystem. This may be
39 used to dump internal state of the l2tp drivers for problem
40 analysis.
41
42 If unsure, say 'Y'.
43
44 To compile this driver as a module, choose M here. The module
45 will be called l2tp_debugfs.
46
34config L2TP_V3 47config L2TP_V3
35 bool "L2TPv3 support (EXPERIMENTAL)" 48 bool "L2TPv3 support (EXPERIMENTAL)"
36 depends on EXPERIMENTAL && L2TP 49 depends on EXPERIMENTAL && L2TP
diff --git a/net/l2tp/Makefile b/net/l2tp/Makefile
index bddbf04f0ed3..110e7bc2de5e 100644
--- a/net/l2tp/Makefile
+++ b/net/l2tp/Makefile
@@ -9,3 +9,4 @@ obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o
9obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip.o 9obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip.o
10obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_V3)) += l2tp_netlink.o 10obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_V3)) += l2tp_netlink.o
11obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_ETH)) += l2tp_eth.o 11obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_ETH)) += l2tp_eth.o
12obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_DEBUGFS)) += l2tp_debugfs.o
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 2974d9ade167..571335530c6f 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -133,7 +133,9 @@ struct l2tp_session {
133 void (*session_close)(struct l2tp_session *session); 133 void (*session_close)(struct l2tp_session *session);
134 void (*ref)(struct l2tp_session *session); 134 void (*ref)(struct l2tp_session *session);
135 void (*deref)(struct l2tp_session *session); 135 void (*deref)(struct l2tp_session *session);
136 136#ifdef CONFIG_L2TP_DEBUGFS
137 void (*show)(struct seq_file *m, void *priv);
138#endif
137 uint8_t priv[0]; /* private data */ 139 uint8_t priv[0]; /* private data */
138}; 140};
139 141
@@ -166,7 +168,9 @@ struct l2tp_tunnel {
166 struct net *l2tp_net; /* the net we belong to */ 168 struct net *l2tp_net; /* the net we belong to */
167 169
168 atomic_t ref_count; 170 atomic_t ref_count;
169 171#ifdef CONFIG_DEBUG_FS
172 void (*show)(struct seq_file *m, void *arg);
173#endif
170 int (*recv_payload_hook)(struct sk_buff *skb); 174 int (*recv_payload_hook)(struct sk_buff *skb);
171 void (*old_sk_destruct)(struct sock *); 175 void (*old_sk_destruct)(struct sock *);
172 struct sock *sock; /* Parent socket */ 176 struct sock *sock; /* Parent socket */
diff --git a/net/l2tp/l2tp_debugfs.c b/net/l2tp/l2tp_debugfs.c
new file mode 100644
index 000000000000..908f10f9720e
--- /dev/null
+++ b/net/l2tp/l2tp_debugfs.c
@@ -0,0 +1,341 @@
1/*
2 * L2TP subsystem debugfs
3 *
4 * Copyright (c) 2010 Katalix Systems Ltd
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/module.h>
13#include <linux/skbuff.h>
14#include <linux/socket.h>
15#include <linux/hash.h>
16#include <linux/l2tp.h>
17#include <linux/in.h>
18#include <linux/etherdevice.h>
19#include <linux/spinlock.h>
20#include <linux/debugfs.h>
21#include <net/sock.h>
22#include <net/ip.h>
23#include <net/icmp.h>
24#include <net/udp.h>
25#include <net/inet_common.h>
26#include <net/inet_hashtables.h>
27#include <net/tcp_states.h>
28#include <net/protocol.h>
29#include <net/xfrm.h>
30#include <net/net_namespace.h>
31#include <net/netns/generic.h>
32
33#include "l2tp_core.h"
34
35static struct dentry *rootdir;
36static struct dentry *tunnels;
37
38struct l2tp_dfs_seq_data {
39 struct net *net;
40 int tunnel_idx; /* current tunnel */
41 int session_idx; /* index of session within current tunnel */
42 struct l2tp_tunnel *tunnel;
43 struct l2tp_session *session; /* NULL means get next tunnel */
44};
45
46static void l2tp_dfs_next_tunnel(struct l2tp_dfs_seq_data *pd)
47{
48 pd->tunnel = l2tp_tunnel_find_nth(pd->net, pd->tunnel_idx);
49 pd->tunnel_idx++;
50}
51
52static void l2tp_dfs_next_session(struct l2tp_dfs_seq_data *pd)
53{
54 pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx);
55 pd->session_idx++;
56
57 if (pd->session == NULL) {
58 pd->session_idx = 0;
59 l2tp_dfs_next_tunnel(pd);
60 }
61
62}
63
64static void *l2tp_dfs_seq_start(struct seq_file *m, loff_t *offs)
65{
66 struct l2tp_dfs_seq_data *pd = SEQ_START_TOKEN;
67 loff_t pos = *offs;
68
69 if (!pos)
70 goto out;
71
72 BUG_ON(m->private == NULL);
73 pd = m->private;
74
75 if (pd->tunnel == NULL)
76 l2tp_dfs_next_tunnel(pd);
77 else
78 l2tp_dfs_next_session(pd);
79
80 /* NULL tunnel and session indicates end of list */
81 if ((pd->tunnel == NULL) && (pd->session == NULL))
82 pd = NULL;
83
84out:
85 return pd;
86}
87
88
89static void *l2tp_dfs_seq_next(struct seq_file *m, void *v, loff_t *pos)
90{
91 (*pos)++;
92 return NULL;
93}
94
95static void l2tp_dfs_seq_stop(struct seq_file *p, void *v)
96{
97 /* nothing to do */
98}
99
100static void l2tp_dfs_seq_tunnel_show(struct seq_file *m, void *v)
101{
102 struct l2tp_tunnel *tunnel = v;
103 int session_count = 0;
104 int hash;
105 struct hlist_node *walk;
106 struct hlist_node *tmp;
107
108 read_lock_bh(&tunnel->hlist_lock);
109 for (hash = 0; hash < L2TP_HASH_SIZE; hash++) {
110 hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) {
111 struct l2tp_session *session;
112
113 session = hlist_entry(walk, struct l2tp_session, hlist);
114 if (session->session_id == 0)
115 continue;
116
117 session_count++;
118 }
119 }
120 read_unlock_bh(&tunnel->hlist_lock);
121
122 seq_printf(m, "\nTUNNEL %u peer %u", tunnel->tunnel_id, tunnel->peer_tunnel_id);
123 if (tunnel->sock) {
124 struct inet_sock *inet = inet_sk(tunnel->sock);
125 seq_printf(m, " from " NIPQUAD_FMT " to " NIPQUAD_FMT "\n",
126 NIPQUAD(inet->inet_saddr), NIPQUAD(inet->inet_daddr));
127 if (tunnel->encap == L2TP_ENCAPTYPE_UDP)
128 seq_printf(m, " source port %hu, dest port %hu\n",
129 ntohs(inet->inet_sport), ntohs(inet->inet_dport));
130 }
131 seq_printf(m, " L2TPv%d, %s\n", tunnel->version,
132 tunnel->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
133 tunnel->encap == L2TP_ENCAPTYPE_IP ? "IP" :
134 "");
135 seq_printf(m, " %d sessions, refcnt %d/%d\n", session_count,
136 tunnel->sock ? atomic_read(&tunnel->sock->sk_refcnt) : 0,
137 atomic_read(&tunnel->ref_count));
138
139 seq_printf(m, " %08x rx %llu/%llu/%llu rx %llu/%llu/%llu\n",
140 tunnel->debug,
141 (unsigned long long)tunnel->stats.tx_packets,
142 (unsigned long long)tunnel->stats.tx_bytes,
143 (unsigned long long)tunnel->stats.tx_errors,
144 (unsigned long long)tunnel->stats.rx_packets,
145 (unsigned long long)tunnel->stats.rx_bytes,
146 (unsigned long long)tunnel->stats.rx_errors);
147
148 if (tunnel->show != NULL)
149 tunnel->show(m, tunnel);
150}
151
152static void l2tp_dfs_seq_session_show(struct seq_file *m, void *v)
153{
154 struct l2tp_session *session = v;
155
156 seq_printf(m, " SESSION %u, peer %u, %s\n", session->session_id,
157 session->peer_session_id,
158 session->pwtype == L2TP_PWTYPE_ETH ? "ETH" :
159 session->pwtype == L2TP_PWTYPE_PPP ? "PPP" :
160 "");
161 if (session->send_seq || session->recv_seq)
162 seq_printf(m, " nr %hu, ns %hu\n", session->nr, session->ns);
163 seq_printf(m, " refcnt %d\n", atomic_read(&session->ref_count));
164 seq_printf(m, " config %d/%d/%c/%c/%s/%s %08x %u\n",
165 session->mtu, session->mru,
166 session->recv_seq ? 'R' : '-',
167 session->send_seq ? 'S' : '-',
168 session->data_seq == 1 ? "IPSEQ" :
169 session->data_seq == 2 ? "DATASEQ" : "-",
170 session->lns_mode ? "LNS" : "LAC",
171 session->debug,
172 jiffies_to_msecs(session->reorder_timeout));
173 seq_printf(m, " offset %hu l2specific %hu/%hu\n",
174 session->offset, session->l2specific_type, session->l2specific_len);
175 if (session->cookie_len) {
176 seq_printf(m, " cookie %02x%02x%02x%02x",
177 session->cookie[0], session->cookie[1],
178 session->cookie[2], session->cookie[3]);
179 if (session->cookie_len == 8)
180 seq_printf(m, "%02x%02x%02x%02x",
181 session->cookie[4], session->cookie[5],
182 session->cookie[6], session->cookie[7]);
183 seq_printf(m, "\n");
184 }
185 if (session->peer_cookie_len) {
186 seq_printf(m, " peer cookie %02x%02x%02x%02x",
187 session->peer_cookie[0], session->peer_cookie[1],
188 session->peer_cookie[2], session->peer_cookie[3]);
189 if (session->peer_cookie_len == 8)
190 seq_printf(m, "%02x%02x%02x%02x",
191 session->peer_cookie[4], session->peer_cookie[5],
192 session->peer_cookie[6], session->peer_cookie[7]);
193 seq_printf(m, "\n");
194 }
195
196 seq_printf(m, " %hu/%hu tx %llu/%llu/%llu rx %llu/%llu/%llu\n",
197 session->nr, session->ns,
198 (unsigned long long)session->stats.tx_packets,
199 (unsigned long long)session->stats.tx_bytes,
200 (unsigned long long)session->stats.tx_errors,
201 (unsigned long long)session->stats.rx_packets,
202 (unsigned long long)session->stats.rx_bytes,
203 (unsigned long long)session->stats.rx_errors);
204
205 if (session->show != NULL)
206 session->show(m, session);
207}
208
209static int l2tp_dfs_seq_show(struct seq_file *m, void *v)
210{
211 struct l2tp_dfs_seq_data *pd = v;
212
213 /* display header on line 1 */
214 if (v == SEQ_START_TOKEN) {
215 seq_puts(m, "TUNNEL ID, peer ID from IP to IP\n");
216 seq_puts(m, " L2TPv2/L2TPv3, UDP/IP\n");
217 seq_puts(m, " sessions session-count, refcnt refcnt/sk->refcnt\n");
218 seq_puts(m, " debug tx-pkts/bytes/errs rx-pkts/bytes/errs\n");
219 seq_puts(m, " SESSION ID, peer ID, PWTYPE\n");
220 seq_puts(m, " refcnt cnt\n");
221 seq_puts(m, " offset OFFSET l2specific TYPE/LEN\n");
222 seq_puts(m, " [ cookie ]\n");
223 seq_puts(m, " [ peer cookie ]\n");
224 seq_puts(m, " config mtu/mru/rcvseq/sendseq/dataseq/lns debug reorderto\n");
225 seq_puts(m, " nr/ns tx-pkts/bytes/errs rx-pkts/bytes/errs\n");
226 goto out;
227 }
228
229 /* Show the tunnel or session context */
230 if (pd->session == NULL)
231 l2tp_dfs_seq_tunnel_show(m, pd->tunnel);
232 else
233 l2tp_dfs_seq_session_show(m, pd->session);
234
235out:
236 return 0;
237}
238
239static const struct seq_operations l2tp_dfs_seq_ops = {
240 .start = l2tp_dfs_seq_start,
241 .next = l2tp_dfs_seq_next,
242 .stop = l2tp_dfs_seq_stop,
243 .show = l2tp_dfs_seq_show,
244};
245
246static int l2tp_dfs_seq_open(struct inode *inode, struct file *file)
247{
248 struct l2tp_dfs_seq_data *pd;
249 struct seq_file *seq;
250 int rc = -ENOMEM;
251
252 pd = kzalloc(GFP_KERNEL, sizeof(*pd));
253 if (pd == NULL)
254 goto out;
255
256 /* Derive the network namespace from the pid opening the
257 * file.
258 */
259 pd->net = get_net_ns_by_pid(current->pid);
260 if (IS_ERR(pd->net)) {
261 rc = -PTR_ERR(pd->net);
262 goto err_free_pd;
263 }
264
265 rc = seq_open(file, &l2tp_dfs_seq_ops);
266 if (rc)
267 goto err_free_net;
268
269 seq = file->private_data;
270 seq->private = pd;
271
272out:
273 return rc;
274
275err_free_net:
276 put_net(pd->net);
277err_free_pd:
278 kfree(pd);
279 goto out;
280}
281
282static int l2tp_dfs_seq_release(struct inode *inode, struct file *file)
283{
284 struct l2tp_dfs_seq_data *pd;
285 struct seq_file *seq;
286
287 seq = file->private_data;
288 pd = seq->private;
289 if (pd->net)
290 put_net(pd->net);
291 kfree(pd);
292 seq_release(inode, file);
293
294 return 0;
295}
296
297static const struct file_operations l2tp_dfs_fops = {
298 .owner = THIS_MODULE,
299 .open = l2tp_dfs_seq_open,
300 .read = seq_read,
301 .llseek = seq_lseek,
302 .release = l2tp_dfs_seq_release,
303};
304
305static int __init l2tp_debugfs_init(void)
306{
307 int rc = 0;
308
309 rootdir = debugfs_create_dir("l2tp", NULL);
310 if (IS_ERR(rootdir)) {
311 rc = PTR_ERR(rootdir);
312 rootdir = NULL;
313 goto out;
314 }
315
316 tunnels = debugfs_create_file("tunnels", 0600, rootdir, NULL, &l2tp_dfs_fops);
317 if (tunnels == NULL)
318 rc = -EIO;
319
320 printk(KERN_INFO "L2TP debugfs support\n");
321
322out:
323 if (rc)
324 printk(KERN_WARNING "l2tp debugfs: unable to init\n");
325
326 return rc;
327}
328
329static void __exit l2tp_debugfs_exit(void)
330{
331 debugfs_remove(tunnels);
332 debugfs_remove(rootdir);
333}
334
335module_init(l2tp_debugfs_init);
336module_exit(l2tp_debugfs_exit);
337
338MODULE_LICENSE("GPL");
339MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
340MODULE_DESCRIPTION("L2TP debugfs driver");
341MODULE_VERSION("1.0");
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
index 755c29729b6f..9848faa3d163 100644
--- a/net/l2tp/l2tp_eth.c
+++ b/net/l2tp/l2tp_eth.c
@@ -172,6 +172,17 @@ static void l2tp_eth_delete(struct l2tp_session *session)
172 } 172 }
173} 173}
174 174
175#ifdef CONFIG_L2TP_DEBUGFS
176static void l2tp_eth_show(struct seq_file *m, void *arg)
177{
178 struct l2tp_session *session = arg;
179 struct l2tp_eth_sess *spriv = l2tp_session_priv(session);
180 struct net_device *dev = spriv->dev;
181
182 seq_printf(m, " interface %s\n", dev->name);
183}
184#endif
185
175static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) 186static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
176{ 187{
177 struct net_device *dev; 188 struct net_device *dev;
@@ -233,6 +244,9 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p
233 priv->tunnel_sock = tunnel->sock; 244 priv->tunnel_sock = tunnel->sock;
234 session->recv_skb = l2tp_eth_dev_recv; 245 session->recv_skb = l2tp_eth_dev_recv;
235 session->session_close = l2tp_eth_delete; 246 session->session_close = l2tp_eth_delete;
247#ifdef CONFIG_L2TP_DEBUGFS
248 session->show = l2tp_eth_show;
249#endif
236 250
237 spriv = l2tp_session_priv(session); 251 spriv = l2tp_session_priv(session);
238 spriv->dev = dev; 252 spriv->dev = dev;
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index d64f081f2b1c..1ef10e4118d2 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -597,6 +597,20 @@ out:
597 return error; 597 return error;
598} 598}
599 599
600#ifdef CONFIG_L2TP_DEBUGFS
601static void pppol2tp_show(struct seq_file *m, void *arg)
602{
603 struct l2tp_session *session = arg;
604 struct pppol2tp_session *ps = l2tp_session_priv(session);
605
606 if (ps) {
607 struct pppox_sock *po = pppox_sk(ps->sock);
608 if (po)
609 seq_printf(m, " interface %s\n", ppp_dev_name(&po->chan));
610 }
611}
612#endif
613
600/* connect() handler. Attach a PPPoX socket to a tunnel UDP socket 614/* connect() handler. Attach a PPPoX socket to a tunnel UDP socket
601 */ 615 */
602static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, 616static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
@@ -734,6 +748,9 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
734 748
735 session->recv_skb = pppol2tp_recv; 749 session->recv_skb = pppol2tp_recv;
736 session->session_close = pppol2tp_session_close; 750 session->session_close = pppol2tp_session_close;
751#ifdef CONFIG_L2TP_DEBUGFS
752 session->show = pppol2tp_show;
753#endif
737 754
738 /* We need to know each time a skb is dropped from the reorder 755 /* We need to know each time a skb is dropped from the reorder
739 * queue. 756 * queue.