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                        const struct ipv6_pinfo *np = inet6_sk(tunnel->sock);
 131
 132                        seq_printf(m, " from %pI6c to %pI6c\n",
 133                                &np->saddr, &tunnel->sock->sk_v6_daddr);
 134                } else
 135#endif
 136                seq_printf(m, " from %pI4 to %pI4\n",
 137                           &inet->inet_saddr, &inet->inet_daddr);
 138                if (tunnel->encap == L2TP_ENCAPTYPE_UDP)
 139                        seq_printf(m, " source port %hu, dest port %hu\n",
 140                                   ntohs(inet->inet_sport), ntohs(inet->inet_dport));
 141        }
 142        seq_printf(m, " L2TPv%d, %s\n", tunnel->version,
 143                   tunnel->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
 144                   tunnel->encap == L2TP_ENCAPTYPE_IP ? "IP" :
 145                   "");
 146        seq_printf(m, " %d sessions, refcnt %d/%d\n", session_count,
 147                   tunnel->sock ? atomic_read(&tunnel->sock->sk_refcnt) : 0,
 148                   atomic_read(&tunnel->ref_count));
 149
 150        seq_printf(m, " %08x rx %ld/%ld/%ld rx %ld/%ld/%ld\n",
 151                   tunnel->debug,
 152                   atomic_long_read(&tunnel->stats.tx_packets),
 153                   atomic_long_read(&tunnel->stats.tx_bytes),
 154                   atomic_long_read(&tunnel->stats.tx_errors),
 155                   atomic_long_read(&tunnel->stats.rx_packets),
 156                   atomic_long_read(&tunnel->stats.rx_bytes),
 157                   atomic_long_read(&tunnel->stats.rx_errors));
 158
 159        if (tunnel->show != NULL)
 160                tunnel->show(m, tunnel);
 161}
 162
 163static void l2tp_dfs_seq_session_show(struct seq_file *m, void *v)
 164{
 165        struct l2tp_session *session = v;
 166
 167        seq_printf(m, "  SESSION %u, peer %u, %s\n", session->session_id,
 168                   session->peer_session_id,
 169                   session->pwtype == L2TP_PWTYPE_ETH ? "ETH" :
 170                   session->pwtype == L2TP_PWTYPE_PPP ? "PPP" :
 171                   "");
 172        if (session->send_seq || session->recv_seq)
 173                seq_printf(m, "   nr %hu, ns %hu\n", session->nr, session->ns);
 174        seq_printf(m, "   refcnt %d\n", atomic_read(&session->ref_count));
 175        seq_printf(m, "   config %d/%d/%c/%c/%s/%s %08x %u\n",
 176                   session->mtu, session->mru,
 177                   session->recv_seq ? 'R' : '-',
 178                   session->send_seq ? 'S' : '-',
 179                   session->data_seq == 1 ? "IPSEQ" :
 180                   session->data_seq == 2 ? "DATASEQ" : "-",
 181                   session->lns_mode ? "LNS" : "LAC",
 182                   session->debug,
 183                   jiffies_to_msecs(session->reorder_timeout));
 184        seq_printf(m, "   offset %hu l2specific %hu/%hu\n",
 185                   session->offset, session->l2specific_type, session->l2specific_len);
 186        if (session->cookie_len) {
 187                seq_printf(m, "   cookie %02x%02x%02x%02x",
 188                           session->cookie[0], session->cookie[1],
 189                           session->cookie[2], session->cookie[3]);
 190                if (session->cookie_len == 8)
 191                        seq_printf(m, "%02x%02x%02x%02x",
 192                                   session->cookie[4], session->cookie[5],
 193                                   session->cookie[6], session->cookie[7]);
 194                seq_printf(m, "\n");
 195        }
 196        if (session->peer_cookie_len) {
 197                seq_printf(m, "   peer cookie %02x%02x%02x%02x",
 198                           session->peer_cookie[0], session->peer_cookie[1],
 199                           session->peer_cookie[2], session->peer_cookie[3]);
 200                if (session->peer_cookie_len == 8)
 201                        seq_printf(m, "%02x%02x%02x%02x",
 202                                   session->peer_cookie[4], session->peer_cookie[5],
 203                                   session->peer_cookie[6], session->peer_cookie[7]);
 204                seq_printf(m, "\n");
 205        }
 206
 207        seq_printf(m, "   %hu/%hu tx %ld/%ld/%ld rx %ld/%ld/%ld\n",
 208                   session->nr, session->ns,
 209                   atomic_long_read(&session->stats.tx_packets),
 210                   atomic_long_read(&session->stats.tx_bytes),
 211                   atomic_long_read(&session->stats.tx_errors),
 212                   atomic_long_read(&session->stats.rx_packets),
 213                   atomic_long_read(&session->stats.rx_bytes),
 214                   atomic_long_read(&session->stats.rx_errors));
 215
 216        if (session->show != NULL)
 217                session->show(m, session);
 218}
 219
 220static int l2tp_dfs_seq_show(struct seq_file *m, void *v)
 221{
 222        struct l2tp_dfs_seq_data *pd = v;
 223
 224        /* display header on line 1 */
 225        if (v == SEQ_START_TOKEN) {
 226                seq_puts(m, "TUNNEL ID, peer ID from IP to IP\n");
 227                seq_puts(m, " L2TPv2/L2TPv3, UDP/IP\n");
 228                seq_puts(m, " sessions session-count, refcnt refcnt/sk->refcnt\n");
 229                seq_puts(m, " debug tx-pkts/bytes/errs rx-pkts/bytes/errs\n");
 230                seq_puts(m, "  SESSION ID, peer ID, PWTYPE\n");
 231                seq_puts(m, "   refcnt cnt\n");
 232                seq_puts(m, "   offset OFFSET l2specific TYPE/LEN\n");
 233                seq_puts(m, "   [ cookie ]\n");
 234                seq_puts(m, "   [ peer cookie ]\n");
 235                seq_puts(m, "   config mtu/mru/rcvseq/sendseq/dataseq/lns debug reorderto\n");
 236                seq_puts(m, "   nr/ns tx-pkts/bytes/errs rx-pkts/bytes/errs\n");
 237                goto out;
 238        }
 239
 240        /* Show the tunnel or session context */
 241        if (pd->session == NULL)
 242                l2tp_dfs_seq_tunnel_show(m, pd->tunnel);
 243        else
 244                l2tp_dfs_seq_session_show(m, pd->session);
 245
 246out:
 247        return 0;
 248}
 249
 250static const struct seq_operations l2tp_dfs_seq_ops = {
 251        .start          = l2tp_dfs_seq_start,
 252        .next           = l2tp_dfs_seq_next,
 253        .stop           = l2tp_dfs_seq_stop,
 254        .show           = l2tp_dfs_seq_show,
 255};
 256
 257static int l2tp_dfs_seq_open(struct inode *inode, struct file *file)
 258{
 259        struct l2tp_dfs_seq_data *pd;
 260        struct seq_file *seq;
 261        int rc = -ENOMEM;
 262
 263        pd = kzalloc(sizeof(*pd), GFP_KERNEL);
 264        if (pd == NULL)
 265                goto out;
 266
 267        /* Derive the network namespace from the pid opening the
 268         * file.
 269         */
 270        pd->net = get_net_ns_by_pid(current->pid);
 271        if (IS_ERR(pd->net)) {
 272                rc = PTR_ERR(pd->net);
 273                goto err_free_pd;
 274        }
 275
 276        rc = seq_open(file, &l2tp_dfs_seq_ops);
 277        if (rc)
 278                goto err_free_net;
 279
 280        seq = file->private_data;
 281        seq->private = pd;
 282
 283out:
 284        return rc;
 285
 286err_free_net:
 287        put_net(pd->net);
 288err_free_pd:
 289        kfree(pd);
 290        goto out;
 291}
 292
 293static int l2tp_dfs_seq_release(struct inode *inode, struct file *file)
 294{
 295        struct l2tp_dfs_seq_data *pd;
 296        struct seq_file *seq;
 297
 298        seq = file->private_data;
 299        pd = seq->private;
 300        if (pd->net)
 301                put_net(pd->net);
 302        kfree(pd);
 303        seq_release(inode, file);
 304
 305        return 0;
 306}
 307
 308static const struct file_operations l2tp_dfs_fops = {
 309        .owner          = THIS_MODULE,
 310        .open           = l2tp_dfs_seq_open,
 311        .read           = seq_read,
 312        .llseek         = seq_lseek,
 313        .release        = l2tp_dfs_seq_release,
 314};
 315
 316static int __init l2tp_debugfs_init(void)
 317{
 318        int rc = 0;
 319
 320        rootdir = debugfs_create_dir("l2tp", NULL);
 321        if (IS_ERR(rootdir)) {
 322                rc = PTR_ERR(rootdir);
 323                rootdir = NULL;
 324                goto out;
 325        }
 326
 327        tunnels = debugfs_create_file("tunnels", 0600, rootdir, NULL, &l2tp_dfs_fops);
 328        if (tunnels == NULL)
 329                rc = -EIO;
 330
 331        pr_info("L2TP debugfs support\n");
 332
 333out:
 334        if (rc)
 335                pr_warn("unable to init\n");
 336
 337        return rc;
 338}
 339
 340static void __exit l2tp_debugfs_exit(void)
 341{
 342        debugfs_remove(tunnels);
 343        debugfs_remove(rootdir);
 344}
 345
 346module_init(l2tp_debugfs_init);
 347module_exit(l2tp_debugfs_exit);
 348
 349MODULE_LICENSE("GPL");
 350MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
 351MODULE_DESCRIPTION("L2TP debugfs driver");
 352MODULE_VERSION("1.0");
 353