blob: 09dec6307206e9a97df682ceeb5f24b48c0c5de2 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * proc_llc.c - proc interface for LLC
3 *
4 * Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
5 * 2002-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
6 *
7 * This program can be redistributed or modified under the terms of the
8 * GNU General Public License as published by the Free Software Foundation.
9 * This program is distributed without any warranty or implied warranty
10 * of merchantability or fitness for a particular purpose.
11 *
12 * See the GNU General Public License for more details.
13 */
14
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/init.h>
16#include <linux/kernel.h>
17#include <linux/proc_fs.h>
18#include <linux/errno.h>
19#include <linux/seq_file.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020020#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <net/sock.h>
22#include <net/llc.h>
23#include <net/llc_c_ac.h>
24#include <net/llc_c_ev.h>
25#include <net/llc_c_st.h>
26#include <net/llc_conn.h>
27
Joe Perches0795af52007-10-03 17:59:30 -070028static void llc_ui_format_mac(struct seq_file *seq, u8 *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -070029{
Johannes Berge1749612008-10-27 15:59:26 -070030 seq_printf(seq, "%pM", addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070031}
32
33static struct sock *llc_get_sk_idx(loff_t pos)
34{
35 struct list_head *sap_entry;
36 struct llc_sap *sap;
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 struct sock *sk = NULL;
Octavian Purdila52d58ae2009-12-26 11:51:05 +000038 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
40 list_for_each(sap_entry, &llc_sap_list) {
41 sap = list_entry(sap_entry, struct llc_sap, node);
42
Octavian Purdilab76f5a82009-12-26 11:51:02 +000043 spin_lock_bh(&sap->sk_lock);
Octavian Purdila52d58ae2009-12-26 11:51:05 +000044 for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++) {
45 struct hlist_nulls_head *head = &sap->sk_laddr_hash[i];
46 struct hlist_nulls_node *node;
47
48 sk_nulls_for_each(sk, node, head) {
49 if (!pos)
50 goto found; /* keep the lock */
51 --pos;
52 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 }
Octavian Purdilab76f5a82009-12-26 11:51:02 +000054 spin_unlock_bh(&sap->sk_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 }
56 sk = NULL;
57found:
58 return sk;
59}
60
61static void *llc_seq_start(struct seq_file *seq, loff_t *pos)
62{
63 loff_t l = *pos;
64
65 read_lock_bh(&llc_sap_list_lock);
66 return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN;
67}
68
Octavian Purdila52d58ae2009-12-26 11:51:05 +000069static struct sock *laddr_hash_next(struct llc_sap *sap, int bucket)
70{
71 struct hlist_nulls_node *node;
72 struct sock *sk = NULL;
73
74 while (++bucket < LLC_SK_LADDR_HASH_ENTRIES)
75 sk_nulls_for_each(sk, node, &sap->sk_laddr_hash[bucket])
76 goto out;
77
78out:
79 return sk;
80}
81
Linus Torvalds1da177e2005-04-16 15:20:36 -070082static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
83{
84 struct sock* sk, *next;
85 struct llc_sock *llc;
86 struct llc_sap *sap;
87
88 ++*pos;
89 if (v == SEQ_START_TOKEN) {
90 sk = llc_get_sk_idx(0);
91 goto out;
92 }
93 sk = v;
Octavian Purdilab76f5a82009-12-26 11:51:02 +000094 next = sk_nulls_next(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 if (next) {
96 sk = next;
97 goto out;
98 }
99 llc = llc_sk(sk);
100 sap = llc->sap;
Octavian Purdila52d58ae2009-12-26 11:51:05 +0000101 sk = laddr_hash_next(sap, llc_sk_laddr_hashfn(sap, &llc->laddr));
102 if (sk)
103 goto out;
Octavian Purdilab76f5a82009-12-26 11:51:02 +0000104 spin_unlock_bh(&sap->sk_lock);
Octavian Purdila52d58ae2009-12-26 11:51:05 +0000105 list_for_each_entry_continue(sap, &llc_sap_list, node) {
Octavian Purdilab76f5a82009-12-26 11:51:02 +0000106 spin_lock_bh(&sap->sk_lock);
Octavian Purdila52d58ae2009-12-26 11:51:05 +0000107 sk = laddr_hash_next(sap, -1);
108 if (sk)
109 break; /* keep the lock */
Octavian Purdilab76f5a82009-12-26 11:51:02 +0000110 spin_unlock_bh(&sap->sk_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 }
112out:
113 return sk;
114}
115
116static void llc_seq_stop(struct seq_file *seq, void *v)
117{
118 if (v && v != SEQ_START_TOKEN) {
119 struct sock *sk = v;
120 struct llc_sock *llc = llc_sk(sk);
121 struct llc_sap *sap = llc->sap;
122
Octavian Purdilab76f5a82009-12-26 11:51:02 +0000123 spin_unlock_bh(&sap->sk_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 }
125 read_unlock_bh(&llc_sap_list_lock);
126}
127
128static int llc_seq_socket_show(struct seq_file *seq, void *v)
129{
130 struct sock* sk;
131 struct llc_sock *llc;
132
133 if (v == SEQ_START_TOKEN) {
134 seq_puts(seq, "SKt Mc local_mac_sap remote_mac_sap "
135 " tx_queue rx_queue st uid link\n");
136 goto out;
137 }
138 sk = v;
139 llc = llc_sk(sk);
140
141 /* FIXME: check if the address is multicast */
142 seq_printf(seq, "%2X %2X ", sk->sk_type, 0);
143
144 if (llc->dev)
145 llc_ui_format_mac(seq, llc->dev->dev_addr);
Joe Perches0795af52007-10-03 17:59:30 -0700146 else {
147 u8 addr[6] = {0,0,0,0,0,0};
148 llc_ui_format_mac(seq, addr);
149 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 seq_printf(seq, "@%02X ", llc->sap->laddr.lsap);
151 llc_ui_format_mac(seq, llc->daddr.mac);
152 seq_printf(seq, "@%02X %8d %8d %2d %3d %4d\n", llc->daddr.lsap,
Eric Dumazet31e6d362009-06-17 19:05:41 -0700153 sk_wmem_alloc_get(sk),
154 sk_rmem_alloc_get(sk) - llc->copied_seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 sk->sk_state,
156 sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : -1,
157 llc->link);
158out:
159 return 0;
160}
161
Jan Engelhardt36cbd3d2009-08-05 10:42:58 -0700162static const char *const llc_conn_state_names[] = {
YOSHIFUJI Hideakid57b1862007-02-09 23:25:01 +0900163 [LLC_CONN_STATE_ADM] = "adm",
164 [LLC_CONN_STATE_SETUP] = "setup",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 [LLC_CONN_STATE_NORMAL] = "normal",
YOSHIFUJI Hideakid57b1862007-02-09 23:25:01 +0900166 [LLC_CONN_STATE_BUSY] = "busy",
167 [LLC_CONN_STATE_REJ] = "rej",
168 [LLC_CONN_STATE_AWAIT] = "await",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 [LLC_CONN_STATE_AWAIT_BUSY] = "await_busy",
170 [LLC_CONN_STATE_AWAIT_REJ] = "await_rej",
171 [LLC_CONN_STATE_D_CONN] = "d_conn",
YOSHIFUJI Hideakid57b1862007-02-09 23:25:01 +0900172 [LLC_CONN_STATE_RESET] = "reset",
173 [LLC_CONN_STATE_ERROR] = "error",
174 [LLC_CONN_STATE_TEMP] = "temp",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175};
176
177static int llc_seq_core_show(struct seq_file *seq, void *v)
178{
179 struct sock* sk;
180 struct llc_sock *llc;
181
182 if (v == SEQ_START_TOKEN) {
183 seq_puts(seq, "Connection list:\n"
184 "dsap state retr txw rxw pf ff sf df rs cs "
185 "tack tpfc trs tbs blog busr\n");
186 goto out;
187 }
188 sk = v;
189 llc = llc_sk(sk);
190
191 seq_printf(seq, " %02X %-10s %3d %3d %3d %2d %2d %2d %2d %2d %2d "
192 "%4d %4d %3d %3d %4d %4d\n",
193 llc->daddr.lsap, llc_conn_state_names[llc->state],
194 llc->retry_count, llc->k, llc->rw, llc->p_flag, llc->f_flag,
195 llc->s_flag, llc->data_flag, llc->remote_busy_flag,
196 llc->cause_flag, timer_pending(&llc->ack_timer.timer),
197 timer_pending(&llc->pf_cycle_timer.timer),
198 timer_pending(&llc->rej_sent_timer.timer),
199 timer_pending(&llc->busy_state_timer.timer),
200 !!sk->sk_backlog.tail, !!sock_owned_by_user(sk));
201out:
202 return 0;
203}
204
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700205static const struct seq_operations llc_seq_socket_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 .start = llc_seq_start,
207 .next = llc_seq_next,
208 .stop = llc_seq_stop,
209 .show = llc_seq_socket_show,
210};
211
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700212static const struct seq_operations llc_seq_core_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 .start = llc_seq_start,
214 .next = llc_seq_next,
215 .stop = llc_seq_stop,
216 .show = llc_seq_core_show,
217};
218
219static int llc_seq_socket_open(struct inode *inode, struct file *file)
220{
221 return seq_open(file, &llc_seq_socket_ops);
222}
223
224static int llc_seq_core_open(struct inode *inode, struct file *file)
225{
226 return seq_open(file, &llc_seq_core_ops);
227}
228
Arjan van de Venda7071d2007-02-12 00:55:36 -0800229static const struct file_operations llc_seq_socket_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 .owner = THIS_MODULE,
231 .open = llc_seq_socket_open,
232 .read = seq_read,
233 .llseek = seq_lseek,
234 .release = seq_release,
235};
236
Arjan van de Venda7071d2007-02-12 00:55:36 -0800237static const struct file_operations llc_seq_core_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 .owner = THIS_MODULE,
239 .open = llc_seq_core_open,
240 .read = seq_read,
241 .llseek = seq_lseek,
242 .release = seq_release,
243};
244
245static struct proc_dir_entry *llc_proc_dir;
246
247int __init llc_proc_init(void)
248{
249 int rc = -ENOMEM;
250 struct proc_dir_entry *p;
251
Eric W. Biederman457c4cb2007-09-12 12:01:34 +0200252 llc_proc_dir = proc_mkdir("llc", init_net.proc_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 if (!llc_proc_dir)
254 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Wang Chen7e021802008-02-28 14:08:54 -0800256 p = proc_create("socket", S_IRUGO, llc_proc_dir, &llc_seq_socket_fops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 if (!p)
258 goto out_socket;
259
Wang Chen7e021802008-02-28 14:08:54 -0800260 p = proc_create("core", S_IRUGO, llc_proc_dir, &llc_seq_core_fops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 if (!p)
262 goto out_core;
263
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 rc = 0;
265out:
266 return rc;
267out_core:
268 remove_proc_entry("socket", llc_proc_dir);
269out_socket:
Eric W. Biederman457c4cb2007-09-12 12:01:34 +0200270 remove_proc_entry("llc", init_net.proc_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 goto out;
272}
273
274void llc_proc_exit(void)
275{
276 remove_proc_entry("socket", llc_proc_dir);
277 remove_proc_entry("core", llc_proc_dir);
Eric W. Biederman457c4cb2007-09-12 12:01:34 +0200278 remove_proc_entry("llc", init_net.proc_net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279}