Linux内核中VLAN的实现过程(3)-proc文件系统(Linux vlan)

网友投稿 463 2022-10-12


Linux内核中VLAN的实现过程(3)-proc文件系统(Linux vlan)

Linux内核中VLAN的实现过程(3)

本节主要关注和解析vlan模块proc文件系统接口功能实现,代码位于net/8021q/vlanproc.c文件中。

接口原型

// 在proc文件系统中创建vlan条目(/proc/net/vlan) int vlan_proc_init(struct net *net); // 移除vlan设备节点 void vlan_proc_rem_dev(struct net_device *vlandev); // 添加vlan设备节点 int vlan_proc_add_dev(struct net_device *vlandev); // 从proc文件系统中删除vlan条目(/proc/net/vlan) void vlan_proc_cleanup(struct net *net);

创建vlan条目

例如,在我的设备上创建两个vlan设备后,查看vlan目录和各个文件内容如下:

root@penyforever-pc:~# vconfig add docker0 1 root@penyforever-pc:~# vconfig add enp2s0 1 root@penyforever-pc:~# cd /proc/net/vlan root@penyforever-pc:/proc/net/vlan# ls config docker0.1 enp2s0.1 root@penyforever-pc:/proc/net/vlan# cat config VLAN Dev name | VLAN ID Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD enp2s0.1 | 1 | enp2s0 docker0.1 | 1 | docker0 root@penyforever-pc:/proc/net/vlan# cat docker0.1 docker0.1 VID: 1 REORDER_HDR: 1 dev->priv_flags: 1001 total frames received 0 total bytes received 0 Broadcast/Multicast Rcvd 0 total frames transmitted 0 total bytes transmitted 0 Device: docker0 INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0 EGRESS priority mappings: root@penyforever-pc:/proc/net/vlan# cat enp2s0.1 enp2s0.1 VID: 1 REORDER_HDR: 1 dev->priv_flags: 1021 total frames received 0 total bytes received 0 Broadcast/Multicast Rcvd 0 total frames transmitted 0 total bytes transmitted 0 Device: enp2s0 INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0 EGRESS priority mappings:

/* * Create /proc/net/vlan entries */ int __net_init vlan_proc_init(struct net *net) { // 根据vlan命名空间id获取vlan proc文件系统信息 struct vlan_net *vn = net_generic(net, vlan_net_id); // 在/proc/net/下创建vlan目录 vn->proc_vlan_dir = proc_net_mkdir(net, name_root, net->proc_net); if (!vn->proc_vlan_dir) goto err; // 在/proc/net/vlan/下创建config文件,并设置文件权限和文件(seq fie)操作函数 vn->proc_vlan_conf = proc_create_net(name_conf, S_IFREG | 0600, vn->proc_vlan_dir, &vlan_seq_ops, sizeof(struct seq_net_private)); if (!vn->proc_vlan_conf) goto err; return 0; err: pr_err("can't create entry in proc filesystem!\n"); 从proc文件系统中删除vlan条目(/proc/net/vlan) vlan_proc_cleanup(net); return -ENOBUFS; }

删除vlan条目

/* * Clean up /proc/net/vlan entries */ void vlan_proc_cleanup(struct net *net) { // 根据vlan命名空间id获取vlan proc文件系统信息 struct vlan_net *vn = net_generic(net, vlan_net_id); // 删除config文件 if (vn->proc_vlan_conf) remove_proc_entry(name_conf, vn->proc_vlan_dir); // 删除vlan目录 if (vn->proc_vlan_dir) remove_proc_entry(name_root, net->proc_net); /* Dynamically added entries should be cleaned up as their vlan_device * is removed, so we should not have to take care of it here... */ }

添加设备节点

/* * Add directory entry for VLAN device. */ int vlan_proc_add_dev(struct net_device *vlandev) { // 获取vlan设备私有信息 struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); // 根据vlan命名空间id获取vlan proc文件系统信息 struct vlan_net *vn = net_generic(dev_net(vlandev), vlan_net_id); // 校验,vlan设备名字不能是“config” if (!strcmp(vlandev->name, name_conf)) return -EINVAL; // 在/proc/vlan/下创建设备节点文件,用来保存vlan设备信息,并设置文件权限和文件(seq file)操作函数 vlan->dent = proc_create_single_data(vlandev->name, S_IFREG | 0600, vn->proc_vlan_dir, vlandev_seq_show, vlandev); if (!vlan->dent) return -ENOBUFS; return 0; }

移除设备节点

/* * Delete directory entry for VLAN device. */ void vlan_proc_rem_dev(struct net_device *vlandev) { // 从/proc/net/vlan/移除vlan设备信息文件 /** NOTE: This will consume the memory pointed to by dent, it seems. */ proc_remove(vlan_dev_priv(vlandev)->dent); // vlan设备私有信息置为空 vlan_dev_priv(vlandev)->dent = NULL; }

seq文件操作函数

一般地,内核通过在procfs文件系统下建立文件来向用户空间提供输出信息,用户空间可以通过任何文本阅读应用查看该文件信息,但是procfs有一个缺陷,如果输出内容大于1个内存页,需要多次读,因此处理起来很难,另外,如果输出太大,速度比较慢,有时会出现一些意想不到的情况,Alexander Viro实现了一套新的功能,使得内核输出大文件信息更容易,该功能出现在2.4.15(包括2.4.15)以后的所有2.4内核以及2.6内核中,尤其是在2.6内核中,已经大量地使用了该功能。

要想使用seq_file功能,开发者需要包含头文件linux/seq_file.h,并定义与设置一个seq_operations结构(类似于file_operations结构),seq_file必须实现四个操作函数:start(), next(), show(), stop()。

/****** Proc filesystem entry points ****************************************/ /* * The following few functions build the content of /proc/net/vlan/config */ // 主要实现初始化工作,在遍历一个链接对象开始时调用,返回一个链接对象的偏移或者SEQ_START_TOKEN(表征这是所有循环的开始) /* start read of /proc/net/vlan/config */ static void *vlan_seq_start(struct seq_file *seq, loff_t *pos) __acquires(rcu) { struct net_device *dev; struct net *net = seq_file_net(seq); loff_t i = 1; // 加rcu读锁 rcu_read_lock(); if (*pos == 0) return SEQ_START_TOKEN; // 遍历网络命名空间下的所有设备,找到偏移pos指向的vlan设备 for_each_netdev_rcu(net, dev) { if (!is_vlan_dev(dev)) continue; if (i++ == *pos) return dev; } return NULL; } // 用来在遍历中寻找下一个链接对象,返回下一个链接对象或者NULL(遍历结束) static void *vlan_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct net_device *dev; struct net *net = seq_file_net(seq); ++*pos; // 找到下一个vlan设备 dev = v; if (v == SEQ_START_TOKEN) dev = net_device_entry(&net->dev_base_head); for_each_netdev_continue_rcu(net, dev) { if (!is_vlan_dev(dev)) continue; return dev; } return NULL; } // 当所有链接对象遍历结束时调用,主要完成一些清理工作 static void vlan_seq_stop(struct seq_file *seq, void *v) __releases(rcu) { // 释放rcu读锁 rcu_read_unlock(); } // 对遍历对象进行查看操作的函数,主要是调用seq_printf(), seq_puts()之类的函数,打印出这个对象节点的信息 static int vlandev_seq_show(struct seq_file *seq, void *offset) { // 获取vlan设备信息 struct net_device *vlandev = (struct net_device *) seq->private; // 获取vlan设备私有信息 const struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); struct rtnl_link_stats64 temp; const struct rtnl_link_stats64 *stats; static const char fmt64[] = "%30s %12llu\n"; int i; if (!is_vlan_dev(vlandev)) return 0; // 获取vlan设备接口统计信息 stats = dev_get_stats(vlandev, &temp); // 输出设备设备名称,vlan id,网络设备接口的标识符(其状态类型被定义在之中),网络设备接口的标识符(但对用户空间不可见) seq_printf(seq, "%s VID: %d REORDER_HDR: %i dev->priv_flags: %llx\n", vlandev->name, vlan->vlan_id, (int)(vlan->flags & 1), vlandev->priv_flags); // 输出接口收到的数据包个数 seq_printf(seq, fmt64, "total frames received", stats->rx_packets); // 输出接口收到的字节数 seq_printf(seq, fmt64, "total bytes received", stats->rx_bytes); // 输出接口收到的广播包和组播包个数 seq_printf(seq, fmt64, "Broadcast/Multicast Rcvd", stats->multicast); seq_puts(seq, "\n"); // 输出接口发送的数据包个数 seq_printf(seq, fmt64, "total frames transmitted", stats->tx_packets); // 输出接口发送的字节数 seq_printf(seq, fmt64, "total bytes transmitted", stats->tx_bytes); // 输出vlan设备宿主设备名称 seq_printf(seq, "Device: %s", vlan->real_dev->name); // 输出入口vlan优先级信息 /* now show all PRIORITY mappings relating to this VLAN */ seq_printf(seq, "\nINGRESS priority mappings: " "0:%u 1:%u 2:%u 3:%u 4:%u 5:%u 6:%u 7:%u\n", vlan->ingress_priority_map[0], vlan->ingress_priority_map[1], vlan->ingress_priority_map[2], vlan->ingress_priority_map[3], vlan->ingress_priority_map[4], vlan->ingress_priority_map[5], vlan->ingress_priority_map[6], vlan->ingress_priority_map[7]); // 输出出口vlan优先级信息 seq_printf(seq, " EGRESS priority mappings: "); for (i = 0; i < 16; i++) { const struct vlan_priority_tci_mapping *mp = vlan->egress_priority_map[i]; while (mp) { seq_printf(seq, "%u:%d ", mp->priority, ((mp->vlan_qos >> 13) & 0x7)); mp = mp->next; } } seq_puts(seq, "\n"); return 0; }


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Linux内核中VLAN的实现过程(4)-设备创建(简述vlan的实现方法)
下一篇:springboot通过注解、接口创建定时任务详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~