llc_proc.c 6.01 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * proc_llc.c - proc interface for LLC
 *
 * Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
 *		 2002-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 *
 * This program can be redistributed or modified under the terms of the
 * GNU General Public License as published by the Free Software Foundation.
 * This program is distributed without any warranty or implied warranty
 * of merchantability or fitness for a particular purpose.
 *
 * See the GNU General Public License for more details.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/errno.h>
#include <linux/seq_file.h>
20
#include <linux/export.h>
21
#include <net/net_namespace.h>
Linus Torvalds's avatar
Linus Torvalds committed
22 23 24 25 26 27 28
#include <net/sock.h>
#include <net/llc.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_st.h>
#include <net/llc_conn.h>

29
static void llc_ui_format_mac(struct seq_file *seq, u8 *addr)
Linus Torvalds's avatar
Linus Torvalds committed
30
{
31
	seq_printf(seq, "%pM", addr);
Linus Torvalds's avatar
Linus Torvalds committed
32 33 34 35 36 37
}

static struct sock *llc_get_sk_idx(loff_t pos)
{
	struct llc_sap *sap;
	struct sock *sk = NULL;
38
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
39

40
	list_for_each_entry_rcu(sap, &llc_sap_list, node) {
41
		spin_lock_bh(&sap->sk_lock);
42 43 44 45 46 47 48 49 50
		for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++) {
			struct hlist_nulls_head *head = &sap->sk_laddr_hash[i];
			struct hlist_nulls_node *node;

			sk_nulls_for_each(sk, node, head) {
				if (!pos)
					goto found; /* keep the lock */
				--pos;
			}
Linus Torvalds's avatar
Linus Torvalds committed
51
		}
52
		spin_unlock_bh(&sap->sk_lock);
Linus Torvalds's avatar
Linus Torvalds committed
53 54 55 56 57 58 59 60 61 62
	}
	sk = NULL;
found:
	return sk;
}

static void *llc_seq_start(struct seq_file *seq, loff_t *pos)
{
	loff_t l = *pos;

63
	rcu_read_lock_bh();
Linus Torvalds's avatar
Linus Torvalds committed
64 65 66
	return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN;
}

67 68 69 70 71 72 73 74 75 76 77 78 79
static struct sock *laddr_hash_next(struct llc_sap *sap, int bucket)
{
	struct hlist_nulls_node *node;
	struct sock *sk = NULL;

	while (++bucket < LLC_SK_LADDR_HASH_ENTRIES)
		sk_nulls_for_each(sk, node, &sap->sk_laddr_hash[bucket])
			goto out;

out:
	return sk;
}

Linus Torvalds's avatar
Linus Torvalds committed
80 81 82 83 84 85 86 87 88 89 90 91
static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
	struct sock* sk, *next;
	struct llc_sock *llc;
	struct llc_sap *sap;

	++*pos;
	if (v == SEQ_START_TOKEN) {
		sk = llc_get_sk_idx(0);
		goto out;
	}
	sk = v;
92
	next = sk_nulls_next(sk);
Linus Torvalds's avatar
Linus Torvalds committed
93 94 95 96 97 98
	if (next) {
		sk = next;
		goto out;
	}
	llc = llc_sk(sk);
	sap = llc->sap;
99 100 101
	sk = laddr_hash_next(sap, llc_sk_laddr_hashfn(sap, &llc->laddr));
	if (sk)
		goto out;
102
	spin_unlock_bh(&sap->sk_lock);
103
	list_for_each_entry_continue_rcu(sap, &llc_sap_list, node) {
104
		spin_lock_bh(&sap->sk_lock);
105 106 107
		sk = laddr_hash_next(sap, -1);
		if (sk)
			break; /* keep the lock */
108
		spin_unlock_bh(&sap->sk_lock);
Linus Torvalds's avatar
Linus Torvalds committed
109 110 111 112 113 114 115 116 117 118 119 120
	}
out:
	return sk;
}

static void llc_seq_stop(struct seq_file *seq, void *v)
{
	if (v && v != SEQ_START_TOKEN) {
		struct sock *sk = v;
		struct llc_sock *llc = llc_sk(sk);
		struct llc_sap *sap = llc->sap;

121
		spin_unlock_bh(&sap->sk_lock);
Linus Torvalds's avatar
Linus Torvalds committed
122
	}
123
	rcu_read_unlock_bh();
Linus Torvalds's avatar
Linus Torvalds committed
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
}

static int llc_seq_socket_show(struct seq_file *seq, void *v)
{
	struct sock* sk;
	struct llc_sock *llc;

	if (v == SEQ_START_TOKEN) {
		seq_puts(seq, "SKt Mc local_mac_sap        remote_mac_sap   "
			      "    tx_queue rx_queue st uid link\n");
		goto out;
	}
	sk = v;
	llc = llc_sk(sk);

	/* FIXME: check if the address is multicast */
	seq_printf(seq, "%2X  %2X ", sk->sk_type, 0);

	if (llc->dev)
		llc_ui_format_mac(seq, llc->dev->dev_addr);
144 145 146 147
	else {
		u8 addr[6] = {0,0,0,0,0,0};
		llc_ui_format_mac(seq, addr);
	}
Linus Torvalds's avatar
Linus Torvalds committed
148 149
	seq_printf(seq, "@%02X ", llc->sap->laddr.lsap);
	llc_ui_format_mac(seq, llc->daddr.mac);
150
	seq_printf(seq, "@%02X %8d %8d %2d %3u %4d\n", llc->daddr.lsap,
151 152
		   sk_wmem_alloc_get(sk),
		   sk_rmem_alloc_get(sk) - llc->copied_seq,
Linus Torvalds's avatar
Linus Torvalds committed
153
		   sk->sk_state,
154
		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
Linus Torvalds's avatar
Linus Torvalds committed
155 156 157 158 159
		   llc->link);
out:
	return 0;
}

160
static const char *const llc_conn_state_names[] = {
161 162
	[LLC_CONN_STATE_ADM] =        "adm",
	[LLC_CONN_STATE_SETUP] =      "setup",
Linus Torvalds's avatar
Linus Torvalds committed
163
	[LLC_CONN_STATE_NORMAL] =     "normal",
164 165 166
	[LLC_CONN_STATE_BUSY] =       "busy",
	[LLC_CONN_STATE_REJ] =        "rej",
	[LLC_CONN_STATE_AWAIT] =      "await",
Linus Torvalds's avatar
Linus Torvalds committed
167 168 169
	[LLC_CONN_STATE_AWAIT_BUSY] = "await_busy",
	[LLC_CONN_STATE_AWAIT_REJ] =  "await_rej",
	[LLC_CONN_STATE_D_CONN]	=     "d_conn",
170 171 172
	[LLC_CONN_STATE_RESET] =      "reset",
	[LLC_CONN_STATE_ERROR] =      "error",
	[LLC_CONN_STATE_TEMP] =       "temp",
Linus Torvalds's avatar
Linus Torvalds committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
};

static int llc_seq_core_show(struct seq_file *seq, void *v)
{
	struct sock* sk;
	struct llc_sock *llc;

	if (v == SEQ_START_TOKEN) {
		seq_puts(seq, "Connection list:\n"
			      "dsap state      retr txw rxw pf ff sf df rs cs "
			      "tack tpfc trs tbs blog busr\n");
		goto out;
	}
	sk = v;
	llc = llc_sk(sk);

	seq_printf(seq, " %02X  %-10s %3d  %3d %3d %2d %2d %2d %2d %2d %2d "
			"%4d %4d %3d %3d %4d %4d\n",
		   llc->daddr.lsap, llc_conn_state_names[llc->state],
		   llc->retry_count, llc->k, llc->rw, llc->p_flag, llc->f_flag,
		   llc->s_flag, llc->data_flag, llc->remote_busy_flag,
		   llc->cause_flag, timer_pending(&llc->ack_timer.timer),
		   timer_pending(&llc->pf_cycle_timer.timer),
		   timer_pending(&llc->rej_sent_timer.timer),
		   timer_pending(&llc->busy_state_timer.timer),
198
		   !!sk->sk_backlog.tail, !!sk->sk_lock.owned);
Linus Torvalds's avatar
Linus Torvalds committed
199 200 201 202
out:
	return 0;
}

203
static const struct seq_operations llc_seq_socket_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
204 205 206 207 208 209
	.start  = llc_seq_start,
	.next   = llc_seq_next,
	.stop   = llc_seq_stop,
	.show   = llc_seq_socket_show,
};

210
static const struct seq_operations llc_seq_core_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
211 212 213 214 215 216 217 218 219 220 221 222 223
	.start  = llc_seq_start,
	.next   = llc_seq_next,
	.stop   = llc_seq_stop,
	.show   = llc_seq_core_show,
};

static struct proc_dir_entry *llc_proc_dir;

int __init llc_proc_init(void)
{
	int rc = -ENOMEM;
	struct proc_dir_entry *p;

224
	llc_proc_dir = proc_mkdir("llc", init_net.proc_net);
Linus Torvalds's avatar
Linus Torvalds committed
225 226 227
	if (!llc_proc_dir)
		goto out;

228
	p = proc_create_seq("socket", 0444, llc_proc_dir, &llc_seq_socket_ops);
Linus Torvalds's avatar
Linus Torvalds committed
229 230 231
	if (!p)
		goto out_socket;

232
	p = proc_create_seq("core", 0444, llc_proc_dir, &llc_seq_core_ops);
Linus Torvalds's avatar
Linus Torvalds committed
233 234 235 236 237 238 239 240 241
	if (!p)
		goto out_core;

	rc = 0;
out:
	return rc;
out_core:
	remove_proc_entry("socket", llc_proc_dir);
out_socket:
242
	remove_proc_entry("llc", init_net.proc_net);
Linus Torvalds's avatar
Linus Torvalds committed
243 244 245 246 247 248 249
	goto out;
}

void llc_proc_exit(void)
{
	remove_proc_entry("socket", llc_proc_dir);
	remove_proc_entry("core", llc_proc_dir);
250
	remove_proc_entry("llc", init_net.proc_net);
Linus Torvalds's avatar
Linus Torvalds committed
251
}