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