Commit | Line | Data |
---|---|---|
a10e763b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* |
3 | * atalk_proc.c - proc support for Appletalk | |
4 | * | |
5 | * Copyright(c) Arnaldo Carvalho de Melo <acme@conectiva.com.br> | |
1da177e4 LT |
6 | */ |
7 | ||
1da177e4 LT |
8 | #include <linux/init.h> |
9 | #include <linux/proc_fs.h> | |
10 | #include <linux/seq_file.h> | |
457c4cbc | 11 | #include <net/net_namespace.h> |
1da177e4 LT |
12 | #include <net/sock.h> |
13 | #include <linux/atalk.h> | |
bc3b2d7f | 14 | #include <linux/export.h> |
1da177e4 LT |
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) | |
ca629f24 | 28 | __acquires(atalk_interfaces_lock) |
1da177e4 LT |
29 | { |
30 | loff_t l = *pos; | |
31 | ||
32 | read_lock_bh(&atalk_interfaces_lock); | |
33 | return l ? atalk_get_interface_idx(--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) | |
ca629f24 | 54 | __releases(atalk_interfaces_lock) |
1da177e4 LT |
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(seq, "Interface Address Networks " | |
65 | "Status\n"); | |
66 | goto out; | |
67 | } | |
68 | ||
69 | iface = v; | |
70 | seq_printf(seq, "%-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) | |
ca629f24 | 89 | __acquires(atalk_routes_lock) |
1da177e4 LT |
90 | { |
91 | loff_t l = *pos; | |
92 | ||
93 | read_lock_bh(&atalk_routes_lock); | |
94 | return l ? atalk_get_route_idx(--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) | |
ca629f24 | 115 | __releases(atalk_routes_lock) |
1da177e4 LT |
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(seq, "Target Router Flags Dev\n"); | |
126 | goto out; | |
127 | } | |
128 | ||
129 | if (atrtr_default.dev) { | |
130 | rt = &atrtr_default; | |
131 | seq_printf(seq, "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(seq, "%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 | ||
1da177e4 | 145 | static void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos) |
ca629f24 | 146 | __acquires(atalk_sockets_lock) |
1da177e4 | 147 | { |
1da177e4 | 148 | read_lock_bh(&atalk_sockets_lock); |
efaffb78 | 149 | return seq_hlist_start_head(&atalk_sockets, *pos); |
1da177e4 LT |
150 | } |
151 | ||
152 | static void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos) | |
153 | { | |
efaffb78 | 154 | return seq_hlist_next(v, &atalk_sockets, pos); |
1da177e4 LT |
155 | } |
156 | ||
157 | static void atalk_seq_socket_stop(struct seq_file *seq, void *v) | |
ca629f24 | 158 | __releases(atalk_sockets_lock) |
1da177e4 LT |
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(seq, "Type Local_addr Remote_addr Tx_queue " | |
170 | "Rx_queue St UID\n"); | |
171 | goto out; | |
172 | } | |
173 | ||
efaffb78 | 174 | s = sk_entry(v); |
1da177e4 LT |
175 | at = at_sk(s); |
176 | ||
177 | seq_printf(seq, "%02X %04X:%02X:%02X %04X:%02X:%02X %08X:%08X " | |
d14c5ab6 | 178 | "%02X %u\n", |
1da177e4 LT |
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, | |
31e6d363 ED |
181 | sk_wmem_alloc_get(s), |
182 | sk_rmem_alloc_get(s), | |
a7cb5a49 EB |
183 | s->sk_state, |
184 | from_kuid_munged(seq_user_ns(seq), sock_i_uid(s))); | |
1da177e4 LT |
185 | out: |
186 | return 0; | |
187 | } | |
188 | ||
56b3d975 | 189 | static const struct seq_operations atalk_seq_interface_ops = { |
1da177e4 LT |
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 | ||
56b3d975 | 196 | static const struct seq_operations atalk_seq_route_ops = { |
1da177e4 LT |
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 | ||
56b3d975 | 203 | static const struct seq_operations atalk_seq_socket_ops = { |
1da177e4 LT |
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 | ||
1da177e4 LT |
210 | int __init atalk_proc_init(void) |
211 | { | |
e2bcd8b0 Y |
212 | if (!proc_mkdir("atalk", init_net.proc_net)) |
213 | return -ENOMEM; | |
1da177e4 | 214 | |
e2bcd8b0 Y |
215 | if (!proc_create_seq("atalk/interface", 0444, init_net.proc_net, |
216 | &atalk_seq_interface_ops)) | |
1da177e4 | 217 | goto out; |
1da177e4 | 218 | |
e2bcd8b0 Y |
219 | if (!proc_create_seq("atalk/route", 0444, init_net.proc_net, |
220 | &atalk_seq_route_ops)) | |
221 | goto out; | |
1da177e4 | 222 | |
e2bcd8b0 Y |
223 | if (!proc_create_seq("atalk/socket", 0444, init_net.proc_net, |
224 | &atalk_seq_socket_ops)) | |
225 | goto out; | |
1da177e4 | 226 | |
e2bcd8b0 Y |
227 | if (!proc_create_seq_private("atalk/arp", 0444, init_net.proc_net, |
228 | &aarp_seq_ops, | |
229 | sizeof(struct aarp_iter_state), NULL)) | |
230 | goto out; | |
1da177e4 | 231 | |
d0f6ba2e VD |
232 | return 0; |
233 | ||
1da177e4 | 234 | out: |
e2bcd8b0 Y |
235 | remove_proc_subtree("atalk", init_net.proc_net); |
236 | return -ENOMEM; | |
1da177e4 LT |
237 | } |
238 | ||
6377f787 | 239 | void atalk_proc_exit(void) |
1da177e4 | 240 | { |
e2bcd8b0 | 241 | remove_proc_subtree("atalk", init_net.proc_net); |
1da177e4 | 242 | } |