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