162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * vlanproc.c VLAN Module. /proc filesystem interface. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This module is completely hardware-independent and provides 662306a36Sopenharmony_ci * access to the router using Linux /proc filesystem. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Ben Greear, <greearb@candelatech.com> coppied from wanproc.c 962306a36Sopenharmony_ci * by: Gene Kozin <genek@compuserve.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright: (c) 1998 Ben Greear 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * ============================================================================ 1462306a36Sopenharmony_ci * Jan 20, 1998 Ben Greear Initial Version 1562306a36Sopenharmony_ci *****************************************************************************/ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/string.h> 2362306a36Sopenharmony_ci#include <linux/proc_fs.h> 2462306a36Sopenharmony_ci#include <linux/seq_file.h> 2562306a36Sopenharmony_ci#include <linux/fs.h> 2662306a36Sopenharmony_ci#include <linux/netdevice.h> 2762306a36Sopenharmony_ci#include <linux/if_vlan.h> 2862306a36Sopenharmony_ci#include <net/net_namespace.h> 2962306a36Sopenharmony_ci#include <net/netns/generic.h> 3062306a36Sopenharmony_ci#include "vlanproc.h" 3162306a36Sopenharmony_ci#include "vlan.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/****** Function Prototypes *************************************************/ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Methods for preparing data for reading proc entries */ 3662306a36Sopenharmony_cistatic int vlan_seq_show(struct seq_file *seq, void *v); 3762306a36Sopenharmony_cistatic void *vlan_seq_start(struct seq_file *seq, loff_t *pos); 3862306a36Sopenharmony_cistatic void *vlan_seq_next(struct seq_file *seq, void *v, loff_t *pos); 3962306a36Sopenharmony_cistatic void vlan_seq_stop(struct seq_file *seq, void *); 4062306a36Sopenharmony_cistatic int vlandev_seq_show(struct seq_file *seq, void *v); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Global Data 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * Names of the proc directory entries 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic const char name_root[] = "vlan"; 5262306a36Sopenharmony_cistatic const char name_conf[] = "config"; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Structures for interfacing with the /proc filesystem. 5662306a36Sopenharmony_ci * VLAN creates its own directory /proc/net/vlan with the following 5762306a36Sopenharmony_ci * entries: 5862306a36Sopenharmony_ci * config device status/configuration 5962306a36Sopenharmony_ci * <device> entry for each device 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * Generic /proc/net/vlan/<file> file and inode operations 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const struct seq_operations vlan_seq_ops = { 6762306a36Sopenharmony_ci .start = vlan_seq_start, 6862306a36Sopenharmony_ci .next = vlan_seq_next, 6962306a36Sopenharmony_ci .stop = vlan_seq_stop, 7062306a36Sopenharmony_ci .show = vlan_seq_show, 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * Proc filesystem directory entries. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* Strings */ 7862306a36Sopenharmony_cistatic const char *const vlan_name_type_str[VLAN_NAME_TYPE_HIGHEST] = { 7962306a36Sopenharmony_ci [VLAN_NAME_TYPE_RAW_PLUS_VID] = "VLAN_NAME_TYPE_RAW_PLUS_VID", 8062306a36Sopenharmony_ci [VLAN_NAME_TYPE_PLUS_VID_NO_PAD] = "VLAN_NAME_TYPE_PLUS_VID_NO_PAD", 8162306a36Sopenharmony_ci [VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD] = "VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD", 8262306a36Sopenharmony_ci [VLAN_NAME_TYPE_PLUS_VID] = "VLAN_NAME_TYPE_PLUS_VID", 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * Interface functions 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * Clean up /proc/net/vlan entries 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_civoid vlan_proc_cleanup(struct net *net) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct vlan_net *vn = net_generic(net, vlan_net_id); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (vn->proc_vlan_conf) 9762306a36Sopenharmony_ci remove_proc_entry(name_conf, vn->proc_vlan_dir); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (vn->proc_vlan_dir) 10062306a36Sopenharmony_ci remove_proc_entry(name_root, net->proc_net); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Dynamically added entries should be cleaned up as their vlan_device 10362306a36Sopenharmony_ci * is removed, so we should not have to take care of it here... 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * Create /proc/net/vlan entries 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciint __net_init vlan_proc_init(struct net *net) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct vlan_net *vn = net_generic(net, vlan_net_id); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci vn->proc_vlan_dir = proc_net_mkdir(net, name_root, net->proc_net); 11662306a36Sopenharmony_ci if (!vn->proc_vlan_dir) 11762306a36Sopenharmony_ci goto err; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci vn->proc_vlan_conf = proc_create_net(name_conf, S_IFREG | 0600, 12062306a36Sopenharmony_ci vn->proc_vlan_dir, &vlan_seq_ops, 12162306a36Sopenharmony_ci sizeof(struct seq_net_private)); 12262306a36Sopenharmony_ci if (!vn->proc_vlan_conf) 12362306a36Sopenharmony_ci goto err; 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cierr: 12762306a36Sopenharmony_ci pr_err("can't create entry in proc filesystem!\n"); 12862306a36Sopenharmony_ci vlan_proc_cleanup(net); 12962306a36Sopenharmony_ci return -ENOBUFS; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * Add directory entry for VLAN device. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciint vlan_proc_add_dev(struct net_device *vlandev) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); 13962306a36Sopenharmony_ci struct vlan_net *vn = net_generic(dev_net(vlandev), vlan_net_id); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (!strcmp(vlandev->name, name_conf)) 14262306a36Sopenharmony_ci return -EINVAL; 14362306a36Sopenharmony_ci vlan->dent = proc_create_single_data(vlandev->name, S_IFREG | 0600, 14462306a36Sopenharmony_ci vn->proc_vlan_dir, vlandev_seq_show, vlandev); 14562306a36Sopenharmony_ci if (!vlan->dent) 14662306a36Sopenharmony_ci return -ENOBUFS; 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * Delete directory entry for VLAN device. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_civoid vlan_proc_rem_dev(struct net_device *vlandev) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci /** NOTE: This will consume the memory pointed to by dent, it seems. */ 15662306a36Sopenharmony_ci proc_remove(vlan_dev_priv(vlandev)->dent); 15762306a36Sopenharmony_ci vlan_dev_priv(vlandev)->dent = NULL; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/****** Proc filesystem entry points ****************************************/ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* 16362306a36Sopenharmony_ci * The following few functions build the content of /proc/net/vlan/config 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* start read of /proc/net/vlan/config */ 16762306a36Sopenharmony_cistatic void *vlan_seq_start(struct seq_file *seq, loff_t *pos) 16862306a36Sopenharmony_ci __acquires(rcu) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct net_device *dev; 17162306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 17262306a36Sopenharmony_ci loff_t i = 1; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci rcu_read_lock(); 17562306a36Sopenharmony_ci if (*pos == 0) 17662306a36Sopenharmony_ci return SEQ_START_TOKEN; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci for_each_netdev_rcu(net, dev) { 17962306a36Sopenharmony_ci if (!is_vlan_dev(dev)) 18062306a36Sopenharmony_ci continue; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (i++ == *pos) 18362306a36Sopenharmony_ci return dev; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return NULL; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void *vlan_seq_next(struct seq_file *seq, void *v, loff_t *pos) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct net_device *dev; 19262306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci ++*pos; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci dev = v; 19762306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 19862306a36Sopenharmony_ci dev = net_device_entry(&net->dev_base_head); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci for_each_netdev_continue_rcu(net, dev) { 20162306a36Sopenharmony_ci if (!is_vlan_dev(dev)) 20262306a36Sopenharmony_ci continue; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return dev; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return NULL; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void vlan_seq_stop(struct seq_file *seq, void *v) 21162306a36Sopenharmony_ci __releases(rcu) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci rcu_read_unlock(); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int vlan_seq_show(struct seq_file *seq, void *v) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 21962306a36Sopenharmony_ci struct vlan_net *vn = net_generic(net, vlan_net_id); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) { 22262306a36Sopenharmony_ci const char *nmtype = NULL; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci seq_puts(seq, "VLAN Dev name | VLAN ID\n"); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (vn->name_type < ARRAY_SIZE(vlan_name_type_str)) 22762306a36Sopenharmony_ci nmtype = vlan_name_type_str[vn->name_type]; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci seq_printf(seq, "Name-Type: %s\n", 23062306a36Sopenharmony_ci nmtype ? nmtype : "UNKNOWN"); 23162306a36Sopenharmony_ci } else { 23262306a36Sopenharmony_ci const struct net_device *vlandev = v; 23362306a36Sopenharmony_ci const struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci seq_printf(seq, "%-15s| %d | %s\n", vlandev->name, 23662306a36Sopenharmony_ci vlan->vlan_id, vlan->real_dev->name); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int vlandev_seq_show(struct seq_file *seq, void *offset) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct net_device *vlandev = (struct net_device *) seq->private; 24462306a36Sopenharmony_ci const struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); 24562306a36Sopenharmony_ci struct rtnl_link_stats64 temp; 24662306a36Sopenharmony_ci const struct rtnl_link_stats64 *stats; 24762306a36Sopenharmony_ci static const char fmt64[] = "%30s %12llu\n"; 24862306a36Sopenharmony_ci int i; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (!is_vlan_dev(vlandev)) 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci stats = dev_get_stats(vlandev, &temp); 25462306a36Sopenharmony_ci seq_printf(seq, 25562306a36Sopenharmony_ci "%s VID: %d REORDER_HDR: %i dev->priv_flags: %llx\n", 25662306a36Sopenharmony_ci vlandev->name, vlan->vlan_id, 25762306a36Sopenharmony_ci (int)(vlan->flags & 1), vlandev->priv_flags); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci seq_printf(seq, fmt64, "total frames received", stats->rx_packets); 26062306a36Sopenharmony_ci seq_printf(seq, fmt64, "total bytes received", stats->rx_bytes); 26162306a36Sopenharmony_ci seq_printf(seq, fmt64, "Broadcast/Multicast Rcvd", stats->multicast); 26262306a36Sopenharmony_ci seq_puts(seq, "\n"); 26362306a36Sopenharmony_ci seq_printf(seq, fmt64, "total frames transmitted", stats->tx_packets); 26462306a36Sopenharmony_ci seq_printf(seq, fmt64, "total bytes transmitted", stats->tx_bytes); 26562306a36Sopenharmony_ci seq_printf(seq, "Device: %s", vlan->real_dev->name); 26662306a36Sopenharmony_ci /* now show all PRIORITY mappings relating to this VLAN */ 26762306a36Sopenharmony_ci seq_printf(seq, "\nINGRESS priority mappings: " 26862306a36Sopenharmony_ci "0:%u 1:%u 2:%u 3:%u 4:%u 5:%u 6:%u 7:%u\n", 26962306a36Sopenharmony_ci vlan->ingress_priority_map[0], 27062306a36Sopenharmony_ci vlan->ingress_priority_map[1], 27162306a36Sopenharmony_ci vlan->ingress_priority_map[2], 27262306a36Sopenharmony_ci vlan->ingress_priority_map[3], 27362306a36Sopenharmony_ci vlan->ingress_priority_map[4], 27462306a36Sopenharmony_ci vlan->ingress_priority_map[5], 27562306a36Sopenharmony_ci vlan->ingress_priority_map[6], 27662306a36Sopenharmony_ci vlan->ingress_priority_map[7]); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci seq_printf(seq, " EGRESS priority mappings: "); 27962306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 28062306a36Sopenharmony_ci const struct vlan_priority_tci_mapping *mp 28162306a36Sopenharmony_ci = vlan->egress_priority_map[i]; 28262306a36Sopenharmony_ci while (mp) { 28362306a36Sopenharmony_ci seq_printf(seq, "%u:%d ", 28462306a36Sopenharmony_ci mp->priority, ((mp->vlan_qos >> 13) & 0x7)); 28562306a36Sopenharmony_ci mp = mp->next; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci seq_puts(seq, "\n"); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 292