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