1 /****************************************************************************** 2 * vlanproc.c VLAN Module. /proc filesystem interface. 3 * 4 * This module is completely hardware-independent and provides 5 * access to the router using Linux /proc filesystem. 6 * 7 * Author: Ben Greear, <greearb@candelatech.com> coppied from wanproc.c 8 * by: Gene Kozin <genek@compuserve.com> 9 * 10 * Copyright: (c) 1998 Ben Greear 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 15 * 2 of the License, or (at your option) any later version. 16 * ============================================================================ 17 * Jan 20, 1998 Ben Greear Initial Version 18 *****************************************************************************/ 19 20 #include <linux/module.h> 21 #include <linux/stddef.h> /* offsetof(), etc. */ 22 #include <linux/errno.h> /* return codes */ 23 #include <linux/kernel.h> 24 #include <linux/slab.h> /* kmalloc(), kfree() */ 25 #include <linux/mm.h> 26 #include <linux/string.h> /* inline mem*, str* functions */ 27 #include <linux/init.h> /* __initfunc et al. */ 28 #include <asm/byteorder.h> /* htons(), etc. */ 29 #include <asm/uaccess.h> /* copy_to_user */ 30 #include <asm/io.h> 31 #include <linux/proc_fs.h> 32 #include <linux/seq_file.h> 33 #include <linux/fs.h> 34 #include <linux/netdevice.h> 35 #include <linux/if_vlan.h> 36 #include "vlanproc.h" 37 #include "vlan.h" 38 39 /****** Function Prototypes *************************************************/ 40 41 /* Methods for preparing data for reading proc entries */ 42 static int vlan_seq_show(struct seq_file *seq, void *v); 43 static void *vlan_seq_start(struct seq_file *seq, loff_t *pos); 44 static void *vlan_seq_next(struct seq_file *seq, void *v, loff_t *pos); 45 static void vlan_seq_stop(struct seq_file *seq, void *); 46 static int vlandev_seq_show(struct seq_file *seq, void *v); 47 48 /* 49 * Global Data 50 */ 51 52 53 /* 54 * Names of the proc directory entries 55 */ 56 57 static const char name_root[] = "vlan"; 58 static const char name_conf[] = "config"; 59 60 /* 61 * Structures for interfacing with the /proc filesystem. 62 * VLAN creates its own directory /proc/net/vlan with the folowing 63 * entries: 64 * config device status/configuration 65 * <device> entry for each device 66 */ 67 68 /* 69 * Generic /proc/net/vlan/<file> file and inode operations 70 */ 71 72 static struct seq_operations vlan_seq_ops = { 73 .start = vlan_seq_start, 74 .next = vlan_seq_next, 75 .stop = vlan_seq_stop, 76 .show = vlan_seq_show, 77 }; 78 79 static int vlan_seq_open(struct inode *inode, struct file *file) 80 { 81 return seq_open(file, &vlan_seq_ops); 82 } 83 84 static struct file_operations vlan_fops = { 85 .owner = THIS_MODULE, 86 .open = vlan_seq_open, 87 .read = seq_read, 88 .llseek = seq_lseek, 89 .release = seq_release, 90 }; 91 92 /* 93 * /proc/net/vlan/<device> file and inode operations 94 */ 95 96 static int vlandev_seq_open(struct inode *inode, struct file *file) 97 { 98 return single_open(file, vlandev_seq_show, PDE(inode)->data); 99 } 100 101 static struct file_operations vlandev_fops = { 102 .owner = THIS_MODULE, 103 .open = vlandev_seq_open, 104 .read = seq_read, 105 .llseek = seq_lseek, 106 .release = single_release, 107 }; 108 109 /* 110 * Proc filesystem derectory entries. 111 */ 112 113 /* 114 * /proc/net/vlan 115 */ 116 117 static struct proc_dir_entry *proc_vlan_dir; 118 119 /* 120 * /proc/net/vlan/config 121 */ 122 123 static struct proc_dir_entry *proc_vlan_conf; 124 125 /* Strings */ 126 static const char *vlan_name_type_str[VLAN_NAME_TYPE_HIGHEST] = { 127 [VLAN_NAME_TYPE_RAW_PLUS_VID] = "VLAN_NAME_TYPE_RAW_PLUS_VID", 128 [VLAN_NAME_TYPE_PLUS_VID_NO_PAD] = "VLAN_NAME_TYPE_PLUS_VID_NO_PAD", 129 [VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD]= "VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD", 130 [VLAN_NAME_TYPE_PLUS_VID] = "VLAN_NAME_TYPE_PLUS_VID", 131 }; 132 /* 133 * Interface functions 134 */ 135 136 /* 137 * Clean up /proc/net/vlan entries 138 */ 139 140 void vlan_proc_cleanup(void) 141 { 142 if (proc_vlan_conf) 143 remove_proc_entry(name_conf, proc_vlan_dir); 144 145 if (proc_vlan_dir) 146 proc_net_remove(name_root); 147 148 /* Dynamically added entries should be cleaned up as their vlan_device 149 * is removed, so we should not have to take care of it here... 150 */ 151 } 152 153 /* 154 * Create /proc/net/vlan entries 155 */ 156 157 int __init vlan_proc_init(void) 158 { 159 proc_vlan_dir = proc_mkdir(name_root, proc_net); 160 if (proc_vlan_dir) { 161 proc_vlan_conf = create_proc_entry(name_conf, 162 S_IFREG|S_IRUSR|S_IWUSR, 163 proc_vlan_dir); 164 if (proc_vlan_conf) { 165 proc_vlan_conf->proc_fops = &vlan_fops; 166 return 0; 167 } 168 } 169 vlan_proc_cleanup(); 170 return -ENOBUFS; 171 } 172 173 /* 174 * Add directory entry for VLAN device. 175 */ 176 177 int vlan_proc_add_dev (struct net_device *vlandev) 178 { 179 struct vlan_dev_info *dev_info = VLAN_DEV_INFO(vlandev); 180 181 if (!(vlandev->priv_flags & IFF_802_1Q_VLAN)) { 182 printk(KERN_ERR 183 "ERROR: vlan_proc_add, device -:%s:- is NOT a VLAN\n", 184 vlandev->name); 185 return -EINVAL; 186 } 187 188 dev_info->dent = create_proc_entry(vlandev->name, 189 S_IFREG|S_IRUSR|S_IWUSR, 190 proc_vlan_dir); 191 if (!dev_info->dent) 192 return -ENOBUFS; 193 194 dev_info->dent->proc_fops = &vlandev_fops; 195 dev_info->dent->data = vlandev; 196 197 #ifdef VLAN_DEBUG 198 printk(KERN_ERR "vlan_proc_add, device -:%s:- being added.\n", 199 vlandev->name); 200 #endif 201 return 0; 202 } 203 204 /* 205 * Delete directory entry for VLAN device. 206 */ 207 int vlan_proc_rem_dev(struct net_device *vlandev) 208 { 209 if (!vlandev) { 210 printk(VLAN_ERR "%s: invalid argument: %p\n", 211 __FUNCTION__, vlandev); 212 return -EINVAL; 213 } 214 215 if (!(vlandev->priv_flags & IFF_802_1Q_VLAN)) { 216 printk(VLAN_DBG "%s: invalid argument, device: %s is not a VLAN device, priv_flags: 0x%4hX.\n", 217 __FUNCTION__, vlandev->name, vlandev->priv_flags); 218 return -EINVAL; 219 } 220 221 #ifdef VLAN_DEBUG 222 printk(VLAN_DBG "%s: dev: %p\n", __FUNCTION__, vlandev); 223 #endif 224 225 /** NOTE: This will consume the memory pointed to by dent, it seems. */ 226 if (VLAN_DEV_INFO(vlandev)->dent) { 227 remove_proc_entry(VLAN_DEV_INFO(vlandev)->dent->name, proc_vlan_dir); 228 VLAN_DEV_INFO(vlandev)->dent = NULL; 229 } 230 231 return 0; 232 } 233 234 /****** Proc filesystem entry points ****************************************/ 235 236 /* 237 * The following few functions build the content of /proc/net/vlan/config 238 */ 239 240 /* starting at dev, find a VLAN device */ 241 static struct net_device *vlan_skip(struct net_device *dev) 242 { 243 while (dev && !(dev->priv_flags & IFF_802_1Q_VLAN)) 244 dev = dev->next; 245 246 return dev; 247 } 248 249 /* start read of /proc/net/vlan/config */ 250 static void *vlan_seq_start(struct seq_file *seq, loff_t *pos) 251 { 252 struct net_device *dev; 253 loff_t i = 1; 254 255 read_lock(&dev_base_lock); 256 257 if (*pos == 0) 258 return SEQ_START_TOKEN; 259 260 for (dev = vlan_skip(dev_base); dev && i < *pos; 261 dev = vlan_skip(dev->next), ++i); 262 263 return (i == *pos) ? dev : NULL; 264 } 265 266 static void *vlan_seq_next(struct seq_file *seq, void *v, loff_t *pos) 267 { 268 ++*pos; 269 270 return vlan_skip((v == SEQ_START_TOKEN) 271 ? dev_base 272 : ((struct net_device *)v)->next); 273 } 274 275 static void vlan_seq_stop(struct seq_file *seq, void *v) 276 { 277 read_unlock(&dev_base_lock); 278 } 279 280 static int vlan_seq_show(struct seq_file *seq, void *v) 281 { 282 if (v == SEQ_START_TOKEN) { 283 const char *nmtype = NULL; 284 285 seq_puts(seq, "VLAN Dev name | VLAN ID\n"); 286 287 if (vlan_name_type < ARRAY_SIZE(vlan_name_type_str)) 288 nmtype = vlan_name_type_str[vlan_name_type]; 289 290 seq_printf(seq, "Name-Type: %s\n", 291 nmtype ? nmtype : "UNKNOWN" ); 292 } else { 293 const struct net_device *vlandev = v; 294 const struct vlan_dev_info *dev_info = VLAN_DEV_INFO(vlandev); 295 296 seq_printf(seq, "%-15s| %d | %s\n", vlandev->name, 297 dev_info->vlan_id, dev_info->real_dev->name); 298 } 299 return 0; 300 } 301 302 static int vlandev_seq_show(struct seq_file *seq, void *offset) 303 { 304 struct net_device *vlandev = (struct net_device *) seq->private; 305 const struct vlan_dev_info *dev_info = VLAN_DEV_INFO(vlandev); 306 struct net_device_stats *stats; 307 static const char fmt[] = "%30s %12lu\n"; 308 int i; 309 310 if ((vlandev == NULL) || (!(vlandev->priv_flags & IFF_802_1Q_VLAN))) 311 return 0; 312 313 seq_printf(seq, "%s VID: %d REORDER_HDR: %i dev->priv_flags: %hx\n", 314 vlandev->name, dev_info->vlan_id, 315 (int)(dev_info->flags & 1), vlandev->priv_flags); 316 317 318 stats = vlan_dev_get_stats(vlandev); 319 320 seq_printf(seq, fmt, "total frames received", stats->rx_packets); 321 seq_printf(seq, fmt, "total bytes received", stats->rx_bytes); 322 seq_printf(seq, fmt, "Broadcast/Multicast Rcvd", stats->multicast); 323 seq_puts(seq, "\n"); 324 seq_printf(seq, fmt, "total frames transmitted", stats->tx_packets); 325 seq_printf(seq, fmt, "total bytes transmitted", stats->tx_bytes); 326 seq_printf(seq, fmt, "total headroom inc", 327 dev_info->cnt_inc_headroom_on_tx); 328 seq_printf(seq, fmt, "total encap on xmit", 329 dev_info->cnt_encap_on_xmit); 330 seq_printf(seq, "Device: %s", dev_info->real_dev->name); 331 /* now show all PRIORITY mappings relating to this VLAN */ 332 seq_printf(seq, 333 "\nINGRESS priority mappings: 0:%lu 1:%lu 2:%lu 3:%lu 4:%lu 5:%lu 6:%lu 7:%lu\n", 334 dev_info->ingress_priority_map[0], 335 dev_info->ingress_priority_map[1], 336 dev_info->ingress_priority_map[2], 337 dev_info->ingress_priority_map[3], 338 dev_info->ingress_priority_map[4], 339 dev_info->ingress_priority_map[5], 340 dev_info->ingress_priority_map[6], 341 dev_info->ingress_priority_map[7]); 342 343 seq_printf(seq, "EGRESSS priority Mappings: "); 344 for (i = 0; i < 16; i++) { 345 const struct vlan_priority_tci_mapping *mp 346 = dev_info->egress_priority_map[i]; 347 while (mp) { 348 seq_printf(seq, "%lu:%hu ", 349 mp->priority, ((mp->vlan_qos >> 13) & 0x7)); 350 mp = mp->next; 351 } 352 } 353 seq_puts(seq, "\n"); 354 355 return 0; 356 } 357