18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * 	atalk_proc.c - proc support for Appletalk
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * 	Copyright(c) Arnaldo Carvalho de Melo <acme@conectiva.com.br>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
108c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
118c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
128c2ecf20Sopenharmony_ci#include <net/sock.h>
138c2ecf20Sopenharmony_ci#include <linux/atalk.h>
148c2ecf20Sopenharmony_ci#include <linux/export.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	struct atalk_iface *i;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	for (i = atalk_interfaces; pos && i; i = i->next)
228c2ecf20Sopenharmony_ci		--pos;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	return i;
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic void *atalk_seq_interface_start(struct seq_file *seq, loff_t *pos)
288c2ecf20Sopenharmony_ci	__acquires(atalk_interfaces_lock)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	loff_t l = *pos;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	read_lock_bh(&atalk_interfaces_lock);
338c2ecf20Sopenharmony_ci	return l ? atalk_get_interface_idx(--l) : SEQ_START_TOKEN;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void *atalk_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct atalk_iface *i;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	++*pos;
418c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
428c2ecf20Sopenharmony_ci		i = NULL;
438c2ecf20Sopenharmony_ci		if (atalk_interfaces)
448c2ecf20Sopenharmony_ci			i = atalk_interfaces;
458c2ecf20Sopenharmony_ci		goto out;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci	i = v;
488c2ecf20Sopenharmony_ci	i = i->next;
498c2ecf20Sopenharmony_ciout:
508c2ecf20Sopenharmony_ci	return i;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void atalk_seq_interface_stop(struct seq_file *seq, void *v)
548c2ecf20Sopenharmony_ci	__releases(atalk_interfaces_lock)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	read_unlock_bh(&atalk_interfaces_lock);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int atalk_seq_interface_show(struct seq_file *seq, void *v)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct atalk_iface *iface;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
648c2ecf20Sopenharmony_ci		seq_puts(seq, "Interface        Address   Networks  "
658c2ecf20Sopenharmony_ci			      "Status\n");
668c2ecf20Sopenharmony_ci		goto out;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	iface = v;
708c2ecf20Sopenharmony_ci	seq_printf(seq, "%-16s %04X:%02X  %04X-%04X  %d\n",
718c2ecf20Sopenharmony_ci		   iface->dev->name, ntohs(iface->address.s_net),
728c2ecf20Sopenharmony_ci		   iface->address.s_node, ntohs(iface->nets.nr_firstnet),
738c2ecf20Sopenharmony_ci		   ntohs(iface->nets.nr_lastnet), iface->status);
748c2ecf20Sopenharmony_ciout:
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic __inline__ struct atalk_route *atalk_get_route_idx(loff_t pos)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct atalk_route *r;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	for (r = atalk_routes; pos && r; r = r->next)
838c2ecf20Sopenharmony_ci		--pos;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return r;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void *atalk_seq_route_start(struct seq_file *seq, loff_t *pos)
898c2ecf20Sopenharmony_ci	__acquires(atalk_routes_lock)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	loff_t l = *pos;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	read_lock_bh(&atalk_routes_lock);
948c2ecf20Sopenharmony_ci	return l ? atalk_get_route_idx(--l) : SEQ_START_TOKEN;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic void *atalk_seq_route_next(struct seq_file *seq, void *v, loff_t *pos)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct atalk_route *r;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	++*pos;
1028c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
1038c2ecf20Sopenharmony_ci		r = NULL;
1048c2ecf20Sopenharmony_ci		if (atalk_routes)
1058c2ecf20Sopenharmony_ci			r = atalk_routes;
1068c2ecf20Sopenharmony_ci		goto out;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	r = v;
1098c2ecf20Sopenharmony_ci	r = r->next;
1108c2ecf20Sopenharmony_ciout:
1118c2ecf20Sopenharmony_ci	return r;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void atalk_seq_route_stop(struct seq_file *seq, void *v)
1158c2ecf20Sopenharmony_ci	__releases(atalk_routes_lock)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	read_unlock_bh(&atalk_routes_lock);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int atalk_seq_route_show(struct seq_file *seq, void *v)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct atalk_route *rt;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
1258c2ecf20Sopenharmony_ci		seq_puts(seq, "Target        Router  Flags Dev\n");
1268c2ecf20Sopenharmony_ci		goto out;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (atrtr_default.dev) {
1308c2ecf20Sopenharmony_ci		rt = &atrtr_default;
1318c2ecf20Sopenharmony_ci		seq_printf(seq, "Default     %04X:%02X  %-4d  %s\n",
1328c2ecf20Sopenharmony_ci			       ntohs(rt->gateway.s_net), rt->gateway.s_node,
1338c2ecf20Sopenharmony_ci			       rt->flags, rt->dev->name);
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	rt = v;
1378c2ecf20Sopenharmony_ci	seq_printf(seq, "%04X:%02X     %04X:%02X  %-4d  %s\n",
1388c2ecf20Sopenharmony_ci		   ntohs(rt->target.s_net), rt->target.s_node,
1398c2ecf20Sopenharmony_ci		   ntohs(rt->gateway.s_net), rt->gateway.s_node,
1408c2ecf20Sopenharmony_ci		   rt->flags, rt->dev->name);
1418c2ecf20Sopenharmony_ciout:
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos)
1468c2ecf20Sopenharmony_ci	__acquires(atalk_sockets_lock)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	read_lock_bh(&atalk_sockets_lock);
1498c2ecf20Sopenharmony_ci	return seq_hlist_start_head(&atalk_sockets, *pos);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	return seq_hlist_next(v, &atalk_sockets, pos);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic void atalk_seq_socket_stop(struct seq_file *seq, void *v)
1588c2ecf20Sopenharmony_ci	__releases(atalk_sockets_lock)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	read_unlock_bh(&atalk_sockets_lock);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int atalk_seq_socket_show(struct seq_file *seq, void *v)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct sock *s;
1668c2ecf20Sopenharmony_ci	struct atalk_sock *at;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
1698c2ecf20Sopenharmony_ci		seq_printf(seq, "Type Local_addr  Remote_addr Tx_queue "
1708c2ecf20Sopenharmony_ci				"Rx_queue St UID\n");
1718c2ecf20Sopenharmony_ci		goto out;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	s = sk_entry(v);
1758c2ecf20Sopenharmony_ci	at = at_sk(s);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	seq_printf(seq, "%02X   %04X:%02X:%02X  %04X:%02X:%02X  %08X:%08X "
1788c2ecf20Sopenharmony_ci			"%02X %u\n",
1798c2ecf20Sopenharmony_ci		   s->sk_type, ntohs(at->src_net), at->src_node, at->src_port,
1808c2ecf20Sopenharmony_ci		   ntohs(at->dest_net), at->dest_node, at->dest_port,
1818c2ecf20Sopenharmony_ci		   sk_wmem_alloc_get(s),
1828c2ecf20Sopenharmony_ci		   sk_rmem_alloc_get(s),
1838c2ecf20Sopenharmony_ci		   s->sk_state,
1848c2ecf20Sopenharmony_ci		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)));
1858c2ecf20Sopenharmony_ciout:
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic const struct seq_operations atalk_seq_interface_ops = {
1908c2ecf20Sopenharmony_ci	.start  = atalk_seq_interface_start,
1918c2ecf20Sopenharmony_ci	.next   = atalk_seq_interface_next,
1928c2ecf20Sopenharmony_ci	.stop   = atalk_seq_interface_stop,
1938c2ecf20Sopenharmony_ci	.show   = atalk_seq_interface_show,
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic const struct seq_operations atalk_seq_route_ops = {
1978c2ecf20Sopenharmony_ci	.start  = atalk_seq_route_start,
1988c2ecf20Sopenharmony_ci	.next   = atalk_seq_route_next,
1998c2ecf20Sopenharmony_ci	.stop   = atalk_seq_route_stop,
2008c2ecf20Sopenharmony_ci	.show   = atalk_seq_route_show,
2018c2ecf20Sopenharmony_ci};
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic const struct seq_operations atalk_seq_socket_ops = {
2048c2ecf20Sopenharmony_ci	.start  = atalk_seq_socket_start,
2058c2ecf20Sopenharmony_ci	.next   = atalk_seq_socket_next,
2068c2ecf20Sopenharmony_ci	.stop   = atalk_seq_socket_stop,
2078c2ecf20Sopenharmony_ci	.show   = atalk_seq_socket_show,
2088c2ecf20Sopenharmony_ci};
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ciint __init atalk_proc_init(void)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	if (!proc_mkdir("atalk", init_net.proc_net))
2138c2ecf20Sopenharmony_ci		return -ENOMEM;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (!proc_create_seq("atalk/interface", 0444, init_net.proc_net,
2168c2ecf20Sopenharmony_ci			    &atalk_seq_interface_ops))
2178c2ecf20Sopenharmony_ci		goto out;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (!proc_create_seq("atalk/route", 0444, init_net.proc_net,
2208c2ecf20Sopenharmony_ci			    &atalk_seq_route_ops))
2218c2ecf20Sopenharmony_ci		goto out;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (!proc_create_seq("atalk/socket", 0444, init_net.proc_net,
2248c2ecf20Sopenharmony_ci			    &atalk_seq_socket_ops))
2258c2ecf20Sopenharmony_ci		goto out;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!proc_create_seq_private("atalk/arp", 0444, init_net.proc_net,
2288c2ecf20Sopenharmony_ci				     &aarp_seq_ops,
2298c2ecf20Sopenharmony_ci				     sizeof(struct aarp_iter_state), NULL))
2308c2ecf20Sopenharmony_ci		goto out;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	return 0;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ciout:
2358c2ecf20Sopenharmony_ci	remove_proc_subtree("atalk", init_net.proc_net);
2368c2ecf20Sopenharmony_ci	return -ENOMEM;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_civoid atalk_proc_exit(void)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	remove_proc_subtree("atalk", init_net.proc_net);
2428c2ecf20Sopenharmony_ci}
243