1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * proc_llc.c - proc interface for LLC
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
5*4882a593Smuzhiyun * 2002-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * This program can be redistributed or modified under the terms of the
8*4882a593Smuzhiyun * GNU General Public License as published by the Free Software Foundation.
9*4882a593Smuzhiyun * This program is distributed without any warranty or implied warranty
10*4882a593Smuzhiyun * of merchantability or fitness for a particular purpose.
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * See the GNU General Public License for more details.
13*4882a593Smuzhiyun */
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/init.h>
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/proc_fs.h>
18*4882a593Smuzhiyun #include <linux/errno.h>
19*4882a593Smuzhiyun #include <linux/seq_file.h>
20*4882a593Smuzhiyun #include <linux/export.h>
21*4882a593Smuzhiyun #include <net/net_namespace.h>
22*4882a593Smuzhiyun #include <net/sock.h>
23*4882a593Smuzhiyun #include <net/llc.h>
24*4882a593Smuzhiyun #include <net/llc_c_ac.h>
25*4882a593Smuzhiyun #include <net/llc_c_ev.h>
26*4882a593Smuzhiyun #include <net/llc_c_st.h>
27*4882a593Smuzhiyun #include <net/llc_conn.h>
28*4882a593Smuzhiyun
llc_ui_format_mac(struct seq_file * seq,u8 * addr)29*4882a593Smuzhiyun static void llc_ui_format_mac(struct seq_file *seq, u8 *addr)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun seq_printf(seq, "%pM", addr);
32*4882a593Smuzhiyun }
33*4882a593Smuzhiyun
llc_get_sk_idx(loff_t pos)34*4882a593Smuzhiyun static struct sock *llc_get_sk_idx(loff_t pos)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun struct llc_sap *sap;
37*4882a593Smuzhiyun struct sock *sk = NULL;
38*4882a593Smuzhiyun int i;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun list_for_each_entry_rcu(sap, &llc_sap_list, node) {
41*4882a593Smuzhiyun spin_lock_bh(&sap->sk_lock);
42*4882a593Smuzhiyun for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++) {
43*4882a593Smuzhiyun struct hlist_nulls_head *head = &sap->sk_laddr_hash[i];
44*4882a593Smuzhiyun struct hlist_nulls_node *node;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun sk_nulls_for_each(sk, node, head) {
47*4882a593Smuzhiyun if (!pos)
48*4882a593Smuzhiyun goto found; /* keep the lock */
49*4882a593Smuzhiyun --pos;
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun spin_unlock_bh(&sap->sk_lock);
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun sk = NULL;
55*4882a593Smuzhiyun found:
56*4882a593Smuzhiyun return sk;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun
llc_seq_start(struct seq_file * seq,loff_t * pos)59*4882a593Smuzhiyun static void *llc_seq_start(struct seq_file *seq, loff_t *pos) __acquires(RCU)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun loff_t l = *pos;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun rcu_read_lock_bh();
64*4882a593Smuzhiyun return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
laddr_hash_next(struct llc_sap * sap,int bucket)67*4882a593Smuzhiyun static struct sock *laddr_hash_next(struct llc_sap *sap, int bucket)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct hlist_nulls_node *node;
70*4882a593Smuzhiyun struct sock *sk = NULL;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun while (++bucket < LLC_SK_LADDR_HASH_ENTRIES)
73*4882a593Smuzhiyun sk_nulls_for_each(sk, node, &sap->sk_laddr_hash[bucket])
74*4882a593Smuzhiyun goto out;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun out:
77*4882a593Smuzhiyun return sk;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
llc_seq_next(struct seq_file * seq,void * v,loff_t * pos)80*4882a593Smuzhiyun static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun struct sock* sk, *next;
83*4882a593Smuzhiyun struct llc_sock *llc;
84*4882a593Smuzhiyun struct llc_sap *sap;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun ++*pos;
87*4882a593Smuzhiyun if (v == SEQ_START_TOKEN) {
88*4882a593Smuzhiyun sk = llc_get_sk_idx(0);
89*4882a593Smuzhiyun goto out;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun sk = v;
92*4882a593Smuzhiyun next = sk_nulls_next(sk);
93*4882a593Smuzhiyun if (next) {
94*4882a593Smuzhiyun sk = next;
95*4882a593Smuzhiyun goto out;
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun llc = llc_sk(sk);
98*4882a593Smuzhiyun sap = llc->sap;
99*4882a593Smuzhiyun sk = laddr_hash_next(sap, llc_sk_laddr_hashfn(sap, &llc->laddr));
100*4882a593Smuzhiyun if (sk)
101*4882a593Smuzhiyun goto out;
102*4882a593Smuzhiyun spin_unlock_bh(&sap->sk_lock);
103*4882a593Smuzhiyun list_for_each_entry_continue_rcu(sap, &llc_sap_list, node) {
104*4882a593Smuzhiyun spin_lock_bh(&sap->sk_lock);
105*4882a593Smuzhiyun sk = laddr_hash_next(sap, -1);
106*4882a593Smuzhiyun if (sk)
107*4882a593Smuzhiyun break; /* keep the lock */
108*4882a593Smuzhiyun spin_unlock_bh(&sap->sk_lock);
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun out:
111*4882a593Smuzhiyun return sk;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
llc_seq_stop(struct seq_file * seq,void * v)114*4882a593Smuzhiyun static void llc_seq_stop(struct seq_file *seq, void *v)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun if (v && v != SEQ_START_TOKEN) {
117*4882a593Smuzhiyun struct sock *sk = v;
118*4882a593Smuzhiyun struct llc_sock *llc = llc_sk(sk);
119*4882a593Smuzhiyun struct llc_sap *sap = llc->sap;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun spin_unlock_bh(&sap->sk_lock);
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun rcu_read_unlock_bh();
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun
llc_seq_socket_show(struct seq_file * seq,void * v)126*4882a593Smuzhiyun static int llc_seq_socket_show(struct seq_file *seq, void *v)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun struct sock* sk;
129*4882a593Smuzhiyun struct llc_sock *llc;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun if (v == SEQ_START_TOKEN) {
132*4882a593Smuzhiyun seq_puts(seq, "SKt Mc local_mac_sap remote_mac_sap "
133*4882a593Smuzhiyun " tx_queue rx_queue st uid link\n");
134*4882a593Smuzhiyun goto out;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun sk = v;
137*4882a593Smuzhiyun llc = llc_sk(sk);
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun /* FIXME: check if the address is multicast */
140*4882a593Smuzhiyun seq_printf(seq, "%2X %2X ", sk->sk_type, 0);
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun if (llc->dev)
143*4882a593Smuzhiyun llc_ui_format_mac(seq, llc->dev->dev_addr);
144*4882a593Smuzhiyun else {
145*4882a593Smuzhiyun u8 addr[6] = {0,0,0,0,0,0};
146*4882a593Smuzhiyun llc_ui_format_mac(seq, addr);
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun seq_printf(seq, "@%02X ", llc->sap->laddr.lsap);
149*4882a593Smuzhiyun llc_ui_format_mac(seq, llc->daddr.mac);
150*4882a593Smuzhiyun seq_printf(seq, "@%02X %8d %8d %2d %3u %4d\n", llc->daddr.lsap,
151*4882a593Smuzhiyun sk_wmem_alloc_get(sk),
152*4882a593Smuzhiyun sk_rmem_alloc_get(sk) - llc->copied_seq,
153*4882a593Smuzhiyun sk->sk_state,
154*4882a593Smuzhiyun from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
155*4882a593Smuzhiyun llc->link);
156*4882a593Smuzhiyun out:
157*4882a593Smuzhiyun return 0;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun static const char *const llc_conn_state_names[] = {
161*4882a593Smuzhiyun [LLC_CONN_STATE_ADM] = "adm",
162*4882a593Smuzhiyun [LLC_CONN_STATE_SETUP] = "setup",
163*4882a593Smuzhiyun [LLC_CONN_STATE_NORMAL] = "normal",
164*4882a593Smuzhiyun [LLC_CONN_STATE_BUSY] = "busy",
165*4882a593Smuzhiyun [LLC_CONN_STATE_REJ] = "rej",
166*4882a593Smuzhiyun [LLC_CONN_STATE_AWAIT] = "await",
167*4882a593Smuzhiyun [LLC_CONN_STATE_AWAIT_BUSY] = "await_busy",
168*4882a593Smuzhiyun [LLC_CONN_STATE_AWAIT_REJ] = "await_rej",
169*4882a593Smuzhiyun [LLC_CONN_STATE_D_CONN] = "d_conn",
170*4882a593Smuzhiyun [LLC_CONN_STATE_RESET] = "reset",
171*4882a593Smuzhiyun [LLC_CONN_STATE_ERROR] = "error",
172*4882a593Smuzhiyun [LLC_CONN_STATE_TEMP] = "temp",
173*4882a593Smuzhiyun };
174*4882a593Smuzhiyun
llc_seq_core_show(struct seq_file * seq,void * v)175*4882a593Smuzhiyun static int llc_seq_core_show(struct seq_file *seq, void *v)
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun struct sock* sk;
178*4882a593Smuzhiyun struct llc_sock *llc;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun if (v == SEQ_START_TOKEN) {
181*4882a593Smuzhiyun seq_puts(seq, "Connection list:\n"
182*4882a593Smuzhiyun "dsap state retr txw rxw pf ff sf df rs cs "
183*4882a593Smuzhiyun "tack tpfc trs tbs blog busr\n");
184*4882a593Smuzhiyun goto out;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun sk = v;
187*4882a593Smuzhiyun llc = llc_sk(sk);
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun seq_printf(seq, " %02X %-10s %3d %3d %3d %2d %2d %2d %2d %2d %2d "
190*4882a593Smuzhiyun "%4d %4d %3d %3d %4d %4d\n",
191*4882a593Smuzhiyun llc->daddr.lsap, llc_conn_state_names[llc->state],
192*4882a593Smuzhiyun llc->retry_count, llc->k, llc->rw, llc->p_flag, llc->f_flag,
193*4882a593Smuzhiyun llc->s_flag, llc->data_flag, llc->remote_busy_flag,
194*4882a593Smuzhiyun llc->cause_flag, timer_pending(&llc->ack_timer.timer),
195*4882a593Smuzhiyun timer_pending(&llc->pf_cycle_timer.timer),
196*4882a593Smuzhiyun timer_pending(&llc->rej_sent_timer.timer),
197*4882a593Smuzhiyun timer_pending(&llc->busy_state_timer.timer),
198*4882a593Smuzhiyun !!sk->sk_backlog.tail, !!sk->sk_lock.owned);
199*4882a593Smuzhiyun out:
200*4882a593Smuzhiyun return 0;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun static const struct seq_operations llc_seq_socket_ops = {
204*4882a593Smuzhiyun .start = llc_seq_start,
205*4882a593Smuzhiyun .next = llc_seq_next,
206*4882a593Smuzhiyun .stop = llc_seq_stop,
207*4882a593Smuzhiyun .show = llc_seq_socket_show,
208*4882a593Smuzhiyun };
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun static const struct seq_operations llc_seq_core_ops = {
211*4882a593Smuzhiyun .start = llc_seq_start,
212*4882a593Smuzhiyun .next = llc_seq_next,
213*4882a593Smuzhiyun .stop = llc_seq_stop,
214*4882a593Smuzhiyun .show = llc_seq_core_show,
215*4882a593Smuzhiyun };
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun static struct proc_dir_entry *llc_proc_dir;
218*4882a593Smuzhiyun
llc_proc_init(void)219*4882a593Smuzhiyun int __init llc_proc_init(void)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun int rc = -ENOMEM;
222*4882a593Smuzhiyun struct proc_dir_entry *p;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun llc_proc_dir = proc_mkdir("llc", init_net.proc_net);
225*4882a593Smuzhiyun if (!llc_proc_dir)
226*4882a593Smuzhiyun goto out;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun p = proc_create_seq("socket", 0444, llc_proc_dir, &llc_seq_socket_ops);
229*4882a593Smuzhiyun if (!p)
230*4882a593Smuzhiyun goto out_socket;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun p = proc_create_seq("core", 0444, llc_proc_dir, &llc_seq_core_ops);
233*4882a593Smuzhiyun if (!p)
234*4882a593Smuzhiyun goto out_core;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun rc = 0;
237*4882a593Smuzhiyun out:
238*4882a593Smuzhiyun return rc;
239*4882a593Smuzhiyun out_core:
240*4882a593Smuzhiyun remove_proc_entry("socket", llc_proc_dir);
241*4882a593Smuzhiyun out_socket:
242*4882a593Smuzhiyun remove_proc_entry("llc", init_net.proc_net);
243*4882a593Smuzhiyun goto out;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun
llc_proc_exit(void)246*4882a593Smuzhiyun void llc_proc_exit(void)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun remove_proc_entry("socket", llc_proc_dir);
249*4882a593Smuzhiyun remove_proc_entry("core", llc_proc_dir);
250*4882a593Smuzhiyun remove_proc_entry("llc", init_net.proc_net);
251*4882a593Smuzhiyun }
252