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