1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * atalk_proc.c - proc support for Appletalk |
4 | * |
5 | * Copyright(c) Arnaldo Carvalho de Melo <acme@conectiva.com.br> |
6 | */ |
7 | |
8 | #include <linux/init.h> |
9 | #include <linux/proc_fs.h> |
10 | #include <linux/seq_file.h> |
11 | #include <net/net_namespace.h> |
12 | #include <net/sock.h> |
13 | #include <linux/atalk.h> |
14 | #include <linux/export.h> |
15 | |
16 | |
17 | static __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos) |
18 | { |
19 | struct atalk_iface *i; |
20 | |
21 | for (i = atalk_interfaces; pos && i; i = i->next) |
22 | --pos; |
23 | |
24 | return i; |
25 | } |
26 | |
27 | static void *atalk_seq_interface_start(struct seq_file *seq, loff_t *pos) |
28 | __acquires(atalk_interfaces_lock) |
29 | { |
30 | loff_t l = *pos; |
31 | |
32 | read_lock_bh(&atalk_interfaces_lock); |
33 | return l ? atalk_get_interface_idx(pos: --l) : SEQ_START_TOKEN; |
34 | } |
35 | |
36 | static void *atalk_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos) |
37 | { |
38 | struct atalk_iface *i; |
39 | |
40 | ++*pos; |
41 | if (v == SEQ_START_TOKEN) { |
42 | i = NULL; |
43 | if (atalk_interfaces) |
44 | i = atalk_interfaces; |
45 | goto out; |
46 | } |
47 | i = v; |
48 | i = i->next; |
49 | out: |
50 | return i; |
51 | } |
52 | |
53 | static void atalk_seq_interface_stop(struct seq_file *seq, void *v) |
54 | __releases(atalk_interfaces_lock) |
55 | { |
56 | read_unlock_bh(&atalk_interfaces_lock); |
57 | } |
58 | |
59 | static int atalk_seq_interface_show(struct seq_file *seq, void *v) |
60 | { |
61 | struct atalk_iface *iface; |
62 | |
63 | if (v == SEQ_START_TOKEN) { |
64 | seq_puts(m: seq, s: "Interface Address Networks " |
65 | "Status\n" ); |
66 | goto out; |
67 | } |
68 | |
69 | iface = v; |
70 | seq_printf(m: seq, fmt: "%-16s %04X:%02X %04X-%04X %d\n" , |
71 | iface->dev->name, ntohs(iface->address.s_net), |
72 | iface->address.s_node, ntohs(iface->nets.nr_firstnet), |
73 | ntohs(iface->nets.nr_lastnet), iface->status); |
74 | out: |
75 | return 0; |
76 | } |
77 | |
78 | static __inline__ struct atalk_route *atalk_get_route_idx(loff_t pos) |
79 | { |
80 | struct atalk_route *r; |
81 | |
82 | for (r = atalk_routes; pos && r; r = r->next) |
83 | --pos; |
84 | |
85 | return r; |
86 | } |
87 | |
88 | static void *atalk_seq_route_start(struct seq_file *seq, loff_t *pos) |
89 | __acquires(atalk_routes_lock) |
90 | { |
91 | loff_t l = *pos; |
92 | |
93 | read_lock_bh(&atalk_routes_lock); |
94 | return l ? atalk_get_route_idx(pos: --l) : SEQ_START_TOKEN; |
95 | } |
96 | |
97 | static void *atalk_seq_route_next(struct seq_file *seq, void *v, loff_t *pos) |
98 | { |
99 | struct atalk_route *r; |
100 | |
101 | ++*pos; |
102 | if (v == SEQ_START_TOKEN) { |
103 | r = NULL; |
104 | if (atalk_routes) |
105 | r = atalk_routes; |
106 | goto out; |
107 | } |
108 | r = v; |
109 | r = r->next; |
110 | out: |
111 | return r; |
112 | } |
113 | |
114 | static void atalk_seq_route_stop(struct seq_file *seq, void *v) |
115 | __releases(atalk_routes_lock) |
116 | { |
117 | read_unlock_bh(&atalk_routes_lock); |
118 | } |
119 | |
120 | static int atalk_seq_route_show(struct seq_file *seq, void *v) |
121 | { |
122 | struct atalk_route *rt; |
123 | |
124 | if (v == SEQ_START_TOKEN) { |
125 | seq_puts(m: seq, s: "Target Router Flags Dev\n" ); |
126 | goto out; |
127 | } |
128 | |
129 | if (atrtr_default.dev) { |
130 | rt = &atrtr_default; |
131 | seq_printf(m: seq, fmt: "Default %04X:%02X %-4d %s\n" , |
132 | ntohs(rt->gateway.s_net), rt->gateway.s_node, |
133 | rt->flags, rt->dev->name); |
134 | } |
135 | |
136 | rt = v; |
137 | seq_printf(m: seq, fmt: "%04X:%02X %04X:%02X %-4d %s\n" , |
138 | ntohs(rt->target.s_net), rt->target.s_node, |
139 | ntohs(rt->gateway.s_net), rt->gateway.s_node, |
140 | rt->flags, rt->dev->name); |
141 | out: |
142 | return 0; |
143 | } |
144 | |
145 | static void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos) |
146 | __acquires(atalk_sockets_lock) |
147 | { |
148 | read_lock_bh(&atalk_sockets_lock); |
149 | return seq_hlist_start_head(head: &atalk_sockets, pos: *pos); |
150 | } |
151 | |
152 | static void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos) |
153 | { |
154 | return seq_hlist_next(v, head: &atalk_sockets, ppos: pos); |
155 | } |
156 | |
157 | static void atalk_seq_socket_stop(struct seq_file *seq, void *v) |
158 | __releases(atalk_sockets_lock) |
159 | { |
160 | read_unlock_bh(&atalk_sockets_lock); |
161 | } |
162 | |
163 | static int atalk_seq_socket_show(struct seq_file *seq, void *v) |
164 | { |
165 | struct sock *s; |
166 | struct atalk_sock *at; |
167 | |
168 | if (v == SEQ_START_TOKEN) { |
169 | seq_printf(m: seq, fmt: "Type Local_addr Remote_addr Tx_queue " |
170 | "Rx_queue St UID\n" ); |
171 | goto out; |
172 | } |
173 | |
174 | s = sk_entry(node: v); |
175 | at = at_sk(sk: s); |
176 | |
177 | seq_printf(m: seq, fmt: "%02X %04X:%02X:%02X %04X:%02X:%02X %08X:%08X " |
178 | "%02X %u\n" , |
179 | s->sk_type, ntohs(at->src_net), at->src_node, at->src_port, |
180 | ntohs(at->dest_net), at->dest_node, at->dest_port, |
181 | sk_wmem_alloc_get(sk: s), |
182 | sk_rmem_alloc_get(sk: s), |
183 | s->sk_state, |
184 | from_kuid_munged(to: seq_user_ns(seq), uid: sock_i_uid(sk: s))); |
185 | out: |
186 | return 0; |
187 | } |
188 | |
189 | static const struct seq_operations atalk_seq_interface_ops = { |
190 | .start = atalk_seq_interface_start, |
191 | .next = atalk_seq_interface_next, |
192 | .stop = atalk_seq_interface_stop, |
193 | .show = atalk_seq_interface_show, |
194 | }; |
195 | |
196 | static const struct seq_operations atalk_seq_route_ops = { |
197 | .start = atalk_seq_route_start, |
198 | .next = atalk_seq_route_next, |
199 | .stop = atalk_seq_route_stop, |
200 | .show = atalk_seq_route_show, |
201 | }; |
202 | |
203 | static const struct seq_operations atalk_seq_socket_ops = { |
204 | .start = atalk_seq_socket_start, |
205 | .next = atalk_seq_socket_next, |
206 | .stop = atalk_seq_socket_stop, |
207 | .show = atalk_seq_socket_show, |
208 | }; |
209 | |
210 | int __init atalk_proc_init(void) |
211 | { |
212 | if (!proc_mkdir("atalk" , init_net.proc_net)) |
213 | return -ENOMEM; |
214 | |
215 | if (!proc_create_seq("atalk/interface" , 0444, init_net.proc_net, |
216 | &atalk_seq_interface_ops)) |
217 | goto out; |
218 | |
219 | if (!proc_create_seq("atalk/route" , 0444, init_net.proc_net, |
220 | &atalk_seq_route_ops)) |
221 | goto out; |
222 | |
223 | if (!proc_create_seq("atalk/socket" , 0444, init_net.proc_net, |
224 | &atalk_seq_socket_ops)) |
225 | goto out; |
226 | |
227 | if (!proc_create_seq_private(name: "atalk/arp" , mode: 0444, parent: init_net.proc_net, |
228 | ops: &aarp_seq_ops, |
229 | state_size: sizeof(struct aarp_iter_state), NULL)) |
230 | goto out; |
231 | |
232 | return 0; |
233 | |
234 | out: |
235 | remove_proc_subtree("atalk" , init_net.proc_net); |
236 | return -ENOMEM; |
237 | } |
238 | |
239 | void atalk_proc_exit(void) |
240 | { |
241 | remove_proc_subtree("atalk" , init_net.proc_net); |
242 | } |
243 | |