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