Skip to content

Commit 0ad6614

Browse files
j-c-hdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent d9e31d1 commit 0ad6614

File tree

6 files changed

+392
-2
lines changed

6 files changed

+392
-2
lines changed

net/l2tp/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ menuconfig L2TP
3131
If you don't need L2TP, say N. To compile all L2TP code as
3232
modules, choose M here.
3333

34+
config 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+
3447
config L2TP_V3
3548
bool "L2TPv3 support (EXPERIMENTAL)"
3649
depends on EXPERIMENTAL && L2TP

net/l2tp/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o
99
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip.o
1010
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_V3)) += l2tp_netlink.o
1111
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_ETH)) += l2tp_eth.o
12+
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_DEBUGFS)) += l2tp_debugfs.o

net/l2tp/l2tp_core.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,9 @@ struct l2tp_session {
133133
void (*session_close)(struct l2tp_session *session);
134134
void (*ref)(struct l2tp_session *session);
135135
void (*deref)(struct l2tp_session *session);
136-
136+
#ifdef CONFIG_L2TP_DEBUGFS
137+
void (*show)(struct seq_file *m, void *priv);
138+
#endif
137139
uint8_t priv[0]; /* private data */
138140
};
139141

@@ -166,7 +168,9 @@ struct l2tp_tunnel {
166168
struct net *l2tp_net; /* the net we belong to */
167169

168170
atomic_t ref_count;
169-
171+
#ifdef CONFIG_DEBUG_FS
172+
void (*show)(struct seq_file *m, void *arg);
173+
#endif
170174
int (*recv_payload_hook)(struct sk_buff *skb);
171175
void (*old_sk_destruct)(struct sock *);
172176
struct sock *sock; /* Parent socket */

net/l2tp/l2tp_debugfs.c

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
35+
static struct dentry *rootdir;
36+
static struct dentry *tunnels;
37+
38+
struct 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+
46+
static 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+
52+
static 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+
64+
static 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+
84+
out:
85+
return pd;
86+
}
87+
88+
89+
static void *l2tp_dfs_seq_next(struct seq_file *m, void *v, loff_t *pos)
90+
{
91+
(*pos)++;
92+
return NULL;
93+
}
94+
95+
static void l2tp_dfs_seq_stop(struct seq_file *p, void *v)
96+
{
97+
/* nothing to do */
98+
}
99+
100+
static 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+
152+
static 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+
209+
static 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+
235+
out:
236+
return 0;
237+
}
238+
239+
static 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+
246+
static 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+
272+
out:
273+
return rc;
274+
275+
err_free_net:
276+
put_net(pd->net);
277+
err_free_pd:
278+
kfree(pd);
279+
goto out;
280+
}
281+
282+
static 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+
297+
static 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+
305+
static 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+
322+
out:
323+
if (rc)
324+
printk(KERN_WARNING "l2tp debugfs: unable to init\n");
325+
326+
return rc;
327+
}
328+
329+
static void __exit l2tp_debugfs_exit(void)
330+
{
331+
debugfs_remove(tunnels);
332+
debugfs_remove(rootdir);
333+
}
334+
335+
module_init(l2tp_debugfs_init);
336+
module_exit(l2tp_debugfs_exit);
337+
338+
MODULE_LICENSE("GPL");
339+
MODULE_AUTHOR("James Chapman <[email protected]>");
340+
MODULE_DESCRIPTION("L2TP debugfs driver");
341+
MODULE_VERSION("1.0");

0 commit comments

Comments
 (0)