xref: /linux/net/core/net-procfs.c (revision 0bef512012b1cd8820f0c9ec80e5f8ceb43fdd59)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/netdevice.h>
3 #include <linux/proc_fs.h>
4 #include <linux/seq_file.h>
5 #include <net/wext.h>
6 
7 #include "dev.h"
8 
9 static void *dev_seq_from_index(struct seq_file *seq, loff_t *pos)
10 {
11 	unsigned long ifindex = *pos;
12 	struct net_device *dev;
13 
14 	for_each_netdev_dump(seq_file_net(seq), dev, ifindex) {
15 		*pos = dev->ifindex;
16 		return dev;
17 	}
18 	return NULL;
19 }
20 
21 static void *dev_seq_start(struct seq_file *seq, loff_t *pos)
22 	__acquires(RCU)
23 {
24 	rcu_read_lock();
25 	if (!*pos)
26 		return SEQ_START_TOKEN;
27 
28 	return dev_seq_from_index(seq, pos);
29 }
30 
31 static void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
32 {
33 	++*pos;
34 	return dev_seq_from_index(seq, pos);
35 }
36 
37 static void dev_seq_stop(struct seq_file *seq, void *v)
38 	__releases(RCU)
39 {
40 	rcu_read_unlock();
41 }
42 
43 static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
44 {
45 	struct rtnl_link_stats64 temp;
46 	const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
47 
48 	seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
49 		   "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
50 		   dev->name, stats->rx_bytes, stats->rx_packets,
51 		   stats->rx_errors,
52 		   stats->rx_dropped + stats->rx_missed_errors,
53 		   stats->rx_fifo_errors,
54 		   stats->rx_length_errors + stats->rx_over_errors +
55 		    stats->rx_crc_errors + stats->rx_frame_errors,
56 		   stats->rx_compressed, stats->multicast,
57 		   stats->tx_bytes, stats->tx_packets,
58 		   stats->tx_errors, stats->tx_dropped,
59 		   stats->tx_fifo_errors, stats->collisions,
60 		   stats->tx_carrier_errors +
61 		    stats->tx_aborted_errors +
62 		    stats->tx_window_errors +
63 		    stats->tx_heartbeat_errors,
64 		   stats->tx_compressed);
65 }
66 
67 /*
68  *	Called from the PROCfs module. This now uses the new arbitrary sized
69  *	/proc/net interface to create /proc/net/dev
70  */
71 static int dev_seq_show(struct seq_file *seq, void *v)
72 {
73 	if (v == SEQ_START_TOKEN)
74 		seq_puts(seq, "Inter-|   Receive                            "
75 			      "                    |  Transmit\n"
76 			      " face |bytes    packets errs drop fifo frame "
77 			      "compressed multicast|bytes    packets errs "
78 			      "drop fifo colls carrier compressed\n");
79 	else
80 		dev_seq_printf_stats(seq, v);
81 	return 0;
82 }
83 
84 static u32 softnet_input_pkt_queue_len(struct softnet_data *sd)
85 {
86 	return skb_queue_len_lockless(&sd->input_pkt_queue);
87 }
88 
89 static u32 softnet_process_queue_len(struct softnet_data *sd)
90 {
91 	return skb_queue_len_lockless(&sd->process_queue);
92 }
93 
94 static struct softnet_data *softnet_get_online(loff_t *pos)
95 {
96 	struct softnet_data *sd = NULL;
97 
98 	while (*pos < nr_cpu_ids)
99 		if (cpu_online(*pos)) {
100 			sd = &per_cpu(softnet_data, *pos);
101 			break;
102 		} else
103 			++*pos;
104 	return sd;
105 }
106 
107 static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
108 {
109 	return softnet_get_online(pos);
110 }
111 
112 static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
113 {
114 	++*pos;
115 	return softnet_get_online(pos);
116 }
117 
118 static void softnet_seq_stop(struct seq_file *seq, void *v)
119 {
120 }
121 
122 static int softnet_seq_show(struct seq_file *seq, void *v)
123 {
124 	struct softnet_data *sd = v;
125 	u32 input_qlen = softnet_input_pkt_queue_len(sd);
126 	u32 process_qlen = softnet_process_queue_len(sd);
127 	unsigned int flow_limit_count = 0;
128 
129 #ifdef CONFIG_NET_FLOW_LIMIT
130 	struct sd_flow_limit *fl;
131 
132 	rcu_read_lock();
133 	fl = rcu_dereference(sd->flow_limit);
134 	if (fl)
135 		flow_limit_count = fl->count;
136 	rcu_read_unlock();
137 #endif
138 
139 	/* the index is the CPU id owing this sd. Since offline CPUs are not
140 	 * displayed, it would be othrwise not trivial for the user-space
141 	 * mapping the data a specific CPU
142 	 */
143 	seq_printf(seq,
144 		   "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x "
145 		   "%08x %08x\n",
146 		   sd->processed, sd->dropped, sd->time_squeeze, 0,
147 		   0, 0, 0, 0, /* was fastroute */
148 		   0,	/* was cpu_collision */
149 		   sd->received_rps, flow_limit_count,
150 		   input_qlen + process_qlen, (int)seq->index,
151 		   input_qlen, process_qlen);
152 	return 0;
153 }
154 
155 static const struct seq_operations dev_seq_ops = {
156 	.start = dev_seq_start,
157 	.next  = dev_seq_next,
158 	.stop  = dev_seq_stop,
159 	.show  = dev_seq_show,
160 };
161 
162 static const struct seq_operations softnet_seq_ops = {
163 	.start = softnet_seq_start,
164 	.next  = softnet_seq_next,
165 	.stop  = softnet_seq_stop,
166 	.show  = softnet_seq_show,
167 };
168 
169 static void *ptype_get_idx(struct seq_file *seq, loff_t pos)
170 {
171 	struct list_head *ptype_list = NULL;
172 	struct packet_type *pt = NULL;
173 	struct net_device *dev;
174 	loff_t i = 0;
175 	int t;
176 
177 	for_each_netdev_rcu(seq_file_net(seq), dev) {
178 		ptype_list = &dev->ptype_all;
179 		list_for_each_entry_rcu(pt, ptype_list, list) {
180 			if (i == pos)
181 				return pt;
182 			++i;
183 		}
184 	}
185 
186 	list_for_each_entry_rcu(pt, &ptype_all, list) {
187 		if (i == pos)
188 			return pt;
189 		++i;
190 	}
191 
192 	for (t = 0; t < PTYPE_HASH_SIZE; t++) {
193 		list_for_each_entry_rcu(pt, &ptype_base[t], list) {
194 			if (i == pos)
195 				return pt;
196 			++i;
197 		}
198 	}
199 	return NULL;
200 }
201 
202 static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
203 	__acquires(RCU)
204 {
205 	rcu_read_lock();
206 	return *pos ? ptype_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
207 }
208 
209 static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
210 {
211 	struct net_device *dev;
212 	struct packet_type *pt;
213 	struct list_head *nxt;
214 	int hash;
215 
216 	++*pos;
217 	if (v == SEQ_START_TOKEN)
218 		return ptype_get_idx(seq, 0);
219 
220 	pt = v;
221 	nxt = pt->list.next;
222 	if (pt->dev) {
223 		if (nxt != &pt->dev->ptype_all)
224 			goto found;
225 
226 		dev = pt->dev;
227 		for_each_netdev_continue_rcu(seq_file_net(seq), dev) {
228 			if (!list_empty(&dev->ptype_all)) {
229 				nxt = dev->ptype_all.next;
230 				goto found;
231 			}
232 		}
233 
234 		nxt = ptype_all.next;
235 		goto ptype_all;
236 	}
237 
238 	if (pt->type == htons(ETH_P_ALL)) {
239 ptype_all:
240 		if (nxt != &ptype_all)
241 			goto found;
242 		hash = 0;
243 		nxt = ptype_base[0].next;
244 	} else
245 		hash = ntohs(pt->type) & PTYPE_HASH_MASK;
246 
247 	while (nxt == &ptype_base[hash]) {
248 		if (++hash >= PTYPE_HASH_SIZE)
249 			return NULL;
250 		nxt = ptype_base[hash].next;
251 	}
252 found:
253 	return list_entry(nxt, struct packet_type, list);
254 }
255 
256 static void ptype_seq_stop(struct seq_file *seq, void *v)
257 	__releases(RCU)
258 {
259 	rcu_read_unlock();
260 }
261 
262 static int ptype_seq_show(struct seq_file *seq, void *v)
263 {
264 	struct packet_type *pt = v;
265 
266 	if (v == SEQ_START_TOKEN)
267 		seq_puts(seq, "Type Device      Function\n");
268 	else if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) &&
269 		 (!pt->dev || net_eq(dev_net(pt->dev), seq_file_net(seq)))) {
270 		if (pt->type == htons(ETH_P_ALL))
271 			seq_puts(seq, "ALL ");
272 		else
273 			seq_printf(seq, "%04x", ntohs(pt->type));
274 
275 		seq_printf(seq, " %-8s %ps\n",
276 			   pt->dev ? pt->dev->name : "", pt->func);
277 	}
278 
279 	return 0;
280 }
281 
282 static const struct seq_operations ptype_seq_ops = {
283 	.start = ptype_seq_start,
284 	.next  = ptype_seq_next,
285 	.stop  = ptype_seq_stop,
286 	.show  = ptype_seq_show,
287 };
288 
289 static int __net_init dev_proc_net_init(struct net *net)
290 {
291 	int rc = -ENOMEM;
292 
293 	if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops,
294 			sizeof(struct seq_net_private)))
295 		goto out;
296 	if (!proc_create_seq("softnet_stat", 0444, net->proc_net,
297 			 &softnet_seq_ops))
298 		goto out_dev;
299 	if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
300 			sizeof(struct seq_net_private)))
301 		goto out_softnet;
302 
303 	if (wext_proc_init(net))
304 		goto out_ptype;
305 	rc = 0;
306 out:
307 	return rc;
308 out_ptype:
309 	remove_proc_entry("ptype", net->proc_net);
310 out_softnet:
311 	remove_proc_entry("softnet_stat", net->proc_net);
312 out_dev:
313 	remove_proc_entry("dev", net->proc_net);
314 	goto out;
315 }
316 
317 static void __net_exit dev_proc_net_exit(struct net *net)
318 {
319 	wext_proc_exit(net);
320 
321 	remove_proc_entry("ptype", net->proc_net);
322 	remove_proc_entry("softnet_stat", net->proc_net);
323 	remove_proc_entry("dev", net->proc_net);
324 }
325 
326 static struct pernet_operations __net_initdata dev_proc_ops = {
327 	.init = dev_proc_net_init,
328 	.exit = dev_proc_net_exit,
329 };
330 
331 static int dev_mc_seq_show(struct seq_file *seq, void *v)
332 {
333 	struct netdev_hw_addr *ha;
334 	struct net_device *dev = v;
335 
336 	if (v == SEQ_START_TOKEN)
337 		return 0;
338 
339 	netif_addr_lock_bh(dev);
340 	netdev_for_each_mc_addr(ha, dev) {
341 		seq_printf(seq, "%-4d %-15s %-5d %-5d %*phN\n",
342 			   dev->ifindex, dev->name,
343 			   ha->refcount, ha->global_use,
344 			   (int)dev->addr_len, ha->addr);
345 	}
346 	netif_addr_unlock_bh(dev);
347 	return 0;
348 }
349 
350 static const struct seq_operations dev_mc_seq_ops = {
351 	.start = dev_seq_start,
352 	.next  = dev_seq_next,
353 	.stop  = dev_seq_stop,
354 	.show  = dev_mc_seq_show,
355 };
356 
357 static int __net_init dev_mc_net_init(struct net *net)
358 {
359 	if (!proc_create_net("dev_mcast", 0, net->proc_net, &dev_mc_seq_ops,
360 			sizeof(struct seq_net_private)))
361 		return -ENOMEM;
362 	return 0;
363 }
364 
365 static void __net_exit dev_mc_net_exit(struct net *net)
366 {
367 	remove_proc_entry("dev_mcast", net->proc_net);
368 }
369 
370 static struct pernet_operations __net_initdata dev_mc_net_ops = {
371 	.init = dev_mc_net_init,
372 	.exit = dev_mc_net_exit,
373 };
374 
375 int __init dev_proc_init(void)
376 {
377 	int ret = register_pernet_subsys(&dev_proc_ops);
378 	if (!ret)
379 		return register_pernet_subsys(&dev_mc_net_ops);
380 	return ret;
381 }
382