Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/l2tp/l2tp_debugfs.c
26146 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* L2TP subsystem debugfs
3
*
4
* Copyright (c) 2010 Katalix Systems Ltd
5
*/
6
7
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8
9
#include <linux/module.h>
10
#include <linux/skbuff.h>
11
#include <linux/socket.h>
12
#include <linux/hash.h>
13
#include <linux/l2tp.h>
14
#include <linux/in.h>
15
#include <linux/etherdevice.h>
16
#include <linux/spinlock.h>
17
#include <linux/debugfs.h>
18
#include <net/sock.h>
19
#include <net/ip.h>
20
#include <net/icmp.h>
21
#include <net/udp.h>
22
#include <net/inet_common.h>
23
#include <net/inet_hashtables.h>
24
#include <net/tcp_states.h>
25
#include <net/protocol.h>
26
#include <net/xfrm.h>
27
#include <net/net_namespace.h>
28
#include <net/netns/generic.h>
29
30
#include "l2tp_core.h"
31
32
static struct dentry *rootdir;
33
34
struct l2tp_dfs_seq_data {
35
struct net *net;
36
netns_tracker ns_tracker;
37
unsigned long tkey; /* lookup key of current tunnel */
38
unsigned long skey; /* lookup key of current session */
39
struct l2tp_tunnel *tunnel;
40
struct l2tp_session *session; /* NULL means get next tunnel */
41
};
42
43
static void l2tp_dfs_next_tunnel(struct l2tp_dfs_seq_data *pd)
44
{
45
/* Drop reference taken during previous invocation */
46
if (pd->tunnel)
47
l2tp_tunnel_put(pd->tunnel);
48
49
pd->tunnel = l2tp_tunnel_get_next(pd->net, &pd->tkey);
50
pd->tkey++;
51
}
52
53
static void l2tp_dfs_next_session(struct l2tp_dfs_seq_data *pd)
54
{
55
/* Drop reference taken during previous invocation */
56
if (pd->session)
57
l2tp_session_put(pd->session);
58
59
pd->session = l2tp_session_get_next(pd->net, pd->tunnel->sock,
60
pd->tunnel->version,
61
pd->tunnel->tunnel_id, &pd->skey);
62
pd->skey++;
63
64
if (!pd->session) {
65
pd->skey = 0;
66
l2tp_dfs_next_tunnel(pd);
67
}
68
}
69
70
static void *l2tp_dfs_seq_start(struct seq_file *m, loff_t *offs)
71
{
72
struct l2tp_dfs_seq_data *pd = SEQ_START_TOKEN;
73
loff_t pos = *offs;
74
75
if (!pos)
76
goto out;
77
78
if (WARN_ON(!m->private)) {
79
pd = NULL;
80
goto out;
81
}
82
pd = m->private;
83
84
if (!pd->tunnel)
85
l2tp_dfs_next_tunnel(pd);
86
else
87
l2tp_dfs_next_session(pd);
88
89
/* NULL tunnel and session indicates end of list */
90
if (!pd->tunnel && !pd->session)
91
pd = NULL;
92
93
out:
94
return pd;
95
}
96
97
static void *l2tp_dfs_seq_next(struct seq_file *m, void *v, loff_t *pos)
98
{
99
(*pos)++;
100
return NULL;
101
}
102
103
static void l2tp_dfs_seq_stop(struct seq_file *p, void *v)
104
{
105
struct l2tp_dfs_seq_data *pd = v;
106
107
if (!pd || pd == SEQ_START_TOKEN)
108
return;
109
110
/* Drop reference taken by last invocation of l2tp_dfs_next_session()
111
* or l2tp_dfs_next_tunnel().
112
*/
113
if (pd->session) {
114
l2tp_session_put(pd->session);
115
pd->session = NULL;
116
}
117
if (pd->tunnel) {
118
l2tp_tunnel_put(pd->tunnel);
119
pd->tunnel = NULL;
120
}
121
}
122
123
static void l2tp_dfs_seq_tunnel_show(struct seq_file *m, void *v)
124
{
125
struct l2tp_tunnel *tunnel = v;
126
struct l2tp_session *session;
127
int session_count = 0;
128
129
rcu_read_lock_bh();
130
list_for_each_entry_rcu(session, &tunnel->session_list, list) {
131
/* Session ID of zero is a dummy/reserved value used by pppol2tp */
132
if (session->session_id == 0)
133
continue;
134
135
session_count++;
136
}
137
rcu_read_unlock_bh();
138
139
seq_printf(m, "\nTUNNEL %u peer %u", tunnel->tunnel_id, tunnel->peer_tunnel_id);
140
if (tunnel->sock) {
141
struct inet_sock *inet = inet_sk(tunnel->sock);
142
143
#if IS_ENABLED(CONFIG_IPV6)
144
if (tunnel->sock->sk_family == AF_INET6) {
145
const struct ipv6_pinfo *np = inet6_sk(tunnel->sock);
146
147
seq_printf(m, " from %pI6c to %pI6c\n",
148
&np->saddr, &tunnel->sock->sk_v6_daddr);
149
}
150
#endif
151
if (tunnel->sock->sk_family == AF_INET)
152
seq_printf(m, " from %pI4 to %pI4\n",
153
&inet->inet_saddr, &inet->inet_daddr);
154
155
if (tunnel->encap == L2TP_ENCAPTYPE_UDP)
156
seq_printf(m, " source port %hu, dest port %hu\n",
157
ntohs(inet->inet_sport), ntohs(inet->inet_dport));
158
}
159
seq_printf(m, " L2TPv%d, %s\n", tunnel->version,
160
tunnel->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
161
tunnel->encap == L2TP_ENCAPTYPE_IP ? "IP" :
162
"");
163
seq_printf(m, " %d sessions, refcnt %d/%d\n", session_count,
164
tunnel->sock ? refcount_read(&tunnel->sock->sk_refcnt) : 0,
165
refcount_read(&tunnel->ref_count));
166
seq_printf(m, " %08x rx %ld/%ld/%ld rx %ld/%ld/%ld\n",
167
0,
168
atomic_long_read(&tunnel->stats.tx_packets),
169
atomic_long_read(&tunnel->stats.tx_bytes),
170
atomic_long_read(&tunnel->stats.tx_errors),
171
atomic_long_read(&tunnel->stats.rx_packets),
172
atomic_long_read(&tunnel->stats.rx_bytes),
173
atomic_long_read(&tunnel->stats.rx_errors));
174
}
175
176
static void l2tp_dfs_seq_session_show(struct seq_file *m, void *v)
177
{
178
struct l2tp_session *session = v;
179
180
seq_printf(m, " SESSION %u, peer %u, %s\n", session->session_id,
181
session->peer_session_id,
182
session->pwtype == L2TP_PWTYPE_ETH ? "ETH" :
183
session->pwtype == L2TP_PWTYPE_PPP ? "PPP" :
184
"");
185
if (session->send_seq || session->recv_seq)
186
seq_printf(m, " nr %u, ns %u\n", session->nr, session->ns);
187
seq_printf(m, " refcnt %d\n", refcount_read(&session->ref_count));
188
seq_printf(m, " config 0/0/%c/%c/-/%s %08x %u\n",
189
session->recv_seq ? 'R' : '-',
190
session->send_seq ? 'S' : '-',
191
session->lns_mode ? "LNS" : "LAC",
192
0,
193
jiffies_to_msecs(session->reorder_timeout));
194
seq_printf(m, " offset 0 l2specific %hu/%d\n",
195
session->l2specific_type, l2tp_get_l2specific_len(session));
196
if (session->cookie_len) {
197
seq_printf(m, " cookie %02x%02x%02x%02x",
198
session->cookie[0], session->cookie[1],
199
session->cookie[2], session->cookie[3]);
200
if (session->cookie_len == 8)
201
seq_printf(m, "%02x%02x%02x%02x",
202
session->cookie[4], session->cookie[5],
203
session->cookie[6], session->cookie[7]);
204
seq_puts(m, "\n");
205
}
206
if (session->peer_cookie_len) {
207
seq_printf(m, " peer cookie %02x%02x%02x%02x",
208
session->peer_cookie[0], session->peer_cookie[1],
209
session->peer_cookie[2], session->peer_cookie[3]);
210
if (session->peer_cookie_len == 8)
211
seq_printf(m, "%02x%02x%02x%02x",
212
session->peer_cookie[4], session->peer_cookie[5],
213
session->peer_cookie[6], session->peer_cookie[7]);
214
seq_puts(m, "\n");
215
}
216
217
seq_printf(m, " %u/%u tx %ld/%ld/%ld rx %ld/%ld/%ld\n",
218
session->nr, session->ns,
219
atomic_long_read(&session->stats.tx_packets),
220
atomic_long_read(&session->stats.tx_bytes),
221
atomic_long_read(&session->stats.tx_errors),
222
atomic_long_read(&session->stats.rx_packets),
223
atomic_long_read(&session->stats.rx_bytes),
224
atomic_long_read(&session->stats.rx_errors));
225
226
if (session->show)
227
session->show(m, session);
228
}
229
230
static int l2tp_dfs_seq_show(struct seq_file *m, void *v)
231
{
232
struct l2tp_dfs_seq_data *pd = v;
233
234
/* display header on line 1 */
235
if (v == SEQ_START_TOKEN) {
236
seq_puts(m, "TUNNEL ID, peer ID from IP to IP\n");
237
seq_puts(m, " L2TPv2/L2TPv3, UDP/IP\n");
238
seq_puts(m, " sessions session-count, refcnt refcnt/sk->refcnt\n");
239
seq_puts(m, " debug tx-pkts/bytes/errs rx-pkts/bytes/errs\n");
240
seq_puts(m, " SESSION ID, peer ID, PWTYPE\n");
241
seq_puts(m, " refcnt cnt\n");
242
seq_puts(m, " offset OFFSET l2specific TYPE/LEN\n");
243
seq_puts(m, " [ cookie ]\n");
244
seq_puts(m, " [ peer cookie ]\n");
245
seq_puts(m, " config mtu/mru/rcvseq/sendseq/dataseq/lns debug reorderto\n");
246
seq_puts(m, " nr/ns tx-pkts/bytes/errs rx-pkts/bytes/errs\n");
247
goto out;
248
}
249
250
if (!pd->session)
251
l2tp_dfs_seq_tunnel_show(m, pd->tunnel);
252
else
253
l2tp_dfs_seq_session_show(m, pd->session);
254
255
out:
256
return 0;
257
}
258
259
static const struct seq_operations l2tp_dfs_seq_ops = {
260
.start = l2tp_dfs_seq_start,
261
.next = l2tp_dfs_seq_next,
262
.stop = l2tp_dfs_seq_stop,
263
.show = l2tp_dfs_seq_show,
264
};
265
266
static int l2tp_dfs_seq_open(struct inode *inode, struct file *file)
267
{
268
struct l2tp_dfs_seq_data *pd;
269
struct seq_file *seq;
270
int rc = -ENOMEM;
271
272
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
273
if (!pd)
274
goto out;
275
276
/* Derive the network namespace from the pid opening the
277
* file.
278
*/
279
pd->net = get_net_ns_by_pid(current->pid);
280
if (IS_ERR(pd->net)) {
281
rc = PTR_ERR(pd->net);
282
goto err_free_pd;
283
}
284
netns_tracker_alloc(pd->net, &pd->ns_tracker, GFP_KERNEL);
285
rc = seq_open(file, &l2tp_dfs_seq_ops);
286
if (rc)
287
goto err_free_net;
288
289
seq = file->private_data;
290
seq->private = pd;
291
292
out:
293
return rc;
294
295
err_free_net:
296
put_net_track(pd->net, &pd->ns_tracker);
297
err_free_pd:
298
kfree(pd);
299
goto out;
300
}
301
302
static int l2tp_dfs_seq_release(struct inode *inode, struct file *file)
303
{
304
struct l2tp_dfs_seq_data *pd;
305
struct seq_file *seq;
306
307
seq = file->private_data;
308
pd = seq->private;
309
if (pd->net)
310
put_net_track(pd->net, &pd->ns_tracker);
311
kfree(pd);
312
seq_release(inode, file);
313
314
return 0;
315
}
316
317
static const struct file_operations l2tp_dfs_fops = {
318
.owner = THIS_MODULE,
319
.open = l2tp_dfs_seq_open,
320
.read = seq_read,
321
.llseek = seq_lseek,
322
.release = l2tp_dfs_seq_release,
323
};
324
325
static int __init l2tp_debugfs_init(void)
326
{
327
rootdir = debugfs_create_dir("l2tp", NULL);
328
329
debugfs_create_file("tunnels", 0600, rootdir, NULL, &l2tp_dfs_fops);
330
331
pr_info("L2TP debugfs support\n");
332
333
return 0;
334
}
335
336
static void __exit l2tp_debugfs_exit(void)
337
{
338
debugfs_remove_recursive(rootdir);
339
}
340
341
module_init(l2tp_debugfs_init);
342
module_exit(l2tp_debugfs_exit);
343
344
MODULE_LICENSE("GPL");
345
MODULE_AUTHOR("James Chapman <[email protected]>");
346
MODULE_DESCRIPTION("L2TP debugfs driver");
347
MODULE_VERSION("1.0");
348
349