多平台统一管理软件接口,如何实现多平台统一管理软件接口
429
2022-10-12
Linux内核中VLAN的实现过程(4)-设备创建(简述vlan的实现方法)
Linux内核中VLAN的实现过程(4)
本节主要关注和解析vlan设备的注册和初始化实现,代码位于net/8021q/vlan.c和net/8021q/vlan_dev.c文件中。
创建VLAN设备
/* Attach a VLAN device to a mac address (ie Ethernet Card). * Returns 0 if the device was created or a negative error code otherwise. */ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) { struct net_device *new_dev; struct vlan_dev_priv *vlan; struct net *net = dev_net(real_dev); struct vlan_net *vn = net_generic(net, vlan_net_id); char name[IFNAMSIZ]; int err; if (vlan_id >= VLAN_VID_MASK) return -ERANGE; // 检查宿主设备是否支持vlan协议以及要创建的vlan id在该设备上是否已经存在 err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id, NULL); if (err < 0) return err; // 根据vlan设备名称风格生成相应的vlan设备名 /* Gotta set up the fields for the device. */ switch (vn->name_type) { case VLAN_NAME_TYPE_RAW_PLUS_VID: /* name will look like: eth1.0005 */ snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, vlan_id); break; case VLAN_NAME_TYPE_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: vlan5 */ snprintf(name, IFNAMSIZ, "vlan%i", vlan_id); break; case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: eth0.5 */ snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, vlan_id); break; case VLAN_NAME_TYPE_PLUS_VID: /* Put our vlan.VID in the name. * Name will look like: vlan0005 */ default: snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id); } // 创建vlan设备,并执行vlan_setup完成设备初始化 new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name, NET_NAME_UNKNOWN, vlan_setup); if (new_dev == NULL) return -ENOBUFS; // 传递宿主设备的网络命名空间和mtu给创建的vlan设备 dev_net_set(new_dev, net); /* need 4 bytes for extra VLAN header info, * hope the underlying device can handle it. */ new_dev->mtu = real_dev->mtu; // 获取创建的vlan设备的私有信息 vlan = vlan_dev_priv(new_dev); // 设置vlan协议、vlan id、宿主设备、proc文件系统中的节点、和设置VLAN_FLAG_REORDER_HDR标记 vlan->vlan_proto = htons(ETH_P_8021Q); vlan->vlan_id = vlan_id; vlan->real_dev = real_dev; vlan->dent = NULL; vlan->flags = VLAN_FLAG_REORDER_HDR; // 注册netlink接口操作集合用于管理该vlan设备 new_dev->rtnl_link_ops = &vlan_link_ops; // 将VLAN虚拟设备注册进内核 err = register_vlan_dev(new_dev, NULL); if (err < 0) goto out_free_newdev; return 0; out_free_newdev: free_netdev(new_dev); return err; } int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack) { // 获取vlan设备私有信息 struct vlan_dev_priv *vlan = vlan_dev_priv(dev); // 宿主设备 struct net_device *real_dev = vlan->real_dev; // vlan设备的vlan id u16 vlan_id = vlan->vlan_id; struct vlan_info *vlan_info; struct vlan_group *grp; int err; // 将指定vlan协议、vlan id添加到宿主设备的vlan_info管理块中 err = vlan_vid_add(real_dev, vlan->vlan_proto, vlan_id); if (err) return err; // 宿主设备保存的的vlan设备信息 vlan_info = rtnl_dereference(real_dev->vlan_info); /* vlan_info should be there now. vlan_vid_add took care of it */ BUG_ON(!vlan_info); // 该宿主设备上的vlan设备组 grp = &vlan_info->grp; // vlan组中vlan设备个数 if (grp->nr_vlan_devs == 0) { // GVRP VLAN协议应用注册 err = vlan_gvrp_init_applicant(real_dev); if (err < 0) goto out_vid_del; // Multiple VLAN协议应用注册 err = vlan_mvrp_init_applicant(real_dev); if (err < 0) goto out_uninit_gvrp; } // 初始化vlan_group->vlan_devices_arrays对应的哈希数组 err = vlan_group_prealloc_vid(grp, vlan->vlan_proto, vlan_id); if (err < 0) goto out_uninit_mvrp; // 注册该网络设备到内核中,注册结果会从通知链中反馈 err = register_netdevice(dev); if (err < 0) goto out_uninit_mvrp; // 将vlan设备加入宿主设备的upper邻接链表中 err = netdev_upper_dev_link(real_dev, dev, extack); if (err) goto out_unregister_netdev; // 将宿主设备的状态传递给vlan设备(比如设备的链路状态) vlan_stacked_transfer_operstate(real_dev, dev, vlan); // 将vlan设备加入事件通知连,通常网络设备会定时地检测设备是否处于可传递状态。当状态发生变化时,会调用netif_carrier_on或者netif_carrier_off来通知内核;从网上设备插拔网线或者另一端的设备关闭或禁止,都会导致连接状态改变;netif_carrier_on—-设备驱动监测到设备传递信号时调用,netif_carrier_off—-设备驱动监测到设备丢失信号时调用,上述两个状态改变函数均会调用linkwatch_fire_event将事件加入到事件队列进行调度 linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */ /* So, got the sucker initialized, now lets place * it into our local structure. */ // 将vlan设备加入vlan组中 vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, dev); // vlan组中的设备数量加一 grp->nr_vlan_devs++; return 0; out_unregister_netdev: // 从内核取消网络设备注册 unregister_netdevice(dev); out_uninit_mvrp: // 注销VRP VLAN协议应用 if (grp->nr_vlan_devs == 0) vlan_mvrp_uninit_applicant(real_dev); out_uninit_gvrp: // 注销Multiple VLAN协议应用 if (grp->nr_vlan_devs == 0) vlan_gvrp_uninit_applicant(real_dev); out_vid_del: vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); return err; }
VLAN设备初始化
void vlan_setup(struct net_device *dev) { // 为vlan设备设置一些链路层基本参数 ether_setup(dev); // 表明这是一个vlan设备,没有队列 dev->priv_flags |= IFF_802_1Q_VLAN | IFF_NO_QUEUE; // 设置支持单播过滤 dev->priv_flags |= IFF_UNICAST_FLT; // 发送方向共享skb dev->priv_flags &= ~IFF_TX_SKB_SHARING; netif_keep_dst(dev); // 注册vlan设备管理操作回调函数集合 dev->netdev_ops = &vlan_netdev_ops; // 注册vlan设备注销回调函数 dev->needs_free_netdev = true; dev->priv_destructor = vlan_dev_free; // 注册使用ethtool工具操作vlan设备的回调函数集合 dev->ethtool_ops = &vlan_ethtool_ops; // 设置mtu范围 dev->min_mtu = 0; dev->max_mtu = ETH_MAX_MTU; // 广播mac清零 eth_zero_addr(dev->broadcast); } static void vlan_dev_free(struct net_device *dev) { // vlan设备私有信息 struct vlan_dev_priv *vlan = vlan_dev_priv(dev); // 释放记录的每个cpu上的统计信息 free_percpu(vlan->vlan_pcpu_stats); vlan->vlan_pcpu_stats = NULL; // 宿主设备引用计数减1(vlan_dev_init中调用dev_hold_track(real_dev)来增加宿主设备引用计数) /* Get rid of the vlan's reference to real_dev */ dev_put_track(vlan->real_dev, &vlan->dev_tracker); }
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~