Linux内核中VLAN的实现过程(5)-设备初始化

网友投稿 410 2022-10-12


Linux内核中VLAN的实现过程(5)-设备初始化

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

本节主要关注和解析vlan设备初始化实现,代码位于net/8021q/vlan_dev.c文件中。

// 使用ethtool工具操作vlan设备的回调函数集合 static const struct ethtool_ops vlan_ethtool_ops = { // 从网卡设备寄存器中读取网卡速率、模式等信息 .get_link_ksettings = vlan_ethtool_get_link_ksettings, // 获取驱动信息 .get_drvinfo = vlan_ethtool_get_drvinfo, // 获取网络连接状态 .get_link = ethtool_op_get_link, // 获取时间戳信息 .get_ts_info = vlan_ethtool_get_ts_info, }; static int vlan_ethtool_get_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings *cmd) { // 获取vlan设备私有信息 const struct vlan_dev_priv *vlan = vlan_dev_priv(dev); // 从宿主网卡获取速率、模式等信息 return __ethtool_get_link_ksettings(vlan->real_dev, cmd); } static void vlan_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { // 驱动名称 strlcpy(info->driver, vlan_fullname, sizeof(info->driver)); // 驱动版本 strlcpy(info->version, vlan_version, sizeof(info->version)); // 固件版本 strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); // 这是在我的设备上显示的vlan设备的驱动信息 root@penyforever-pc:~# ethtool -i enp2s0.1 driver: 802.1Q VLAN Support version: 1.8 firmware-version: N/A expansion-rom-version: bus-info: supports-statistics: no supports-test: no supports-eeprom-access: no supports-register-dump: no supports-priv-flags: no } static int vlan_ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) { // 获取vlan设备私有信息 const struct vlan_dev_priv *vlan = vlan_dev_priv(dev); // 获取宿主设备的ethtool操作函数集合 const struct ethtool_ops *ops = vlan->real_dev->ethtool_ops; // 获取宿主设备的PHY设备 struct phy_device *phydev = vlan->real_dev->phydev; // 判断PHY设备是否报告时间戳或具有PTP硬件时钟功能 if (phy_has_tsinfo(phydev)) { // 从PHY设备获取时间戳 return phy_ts_info(phydev, info); } else if (ops->get_ts_info) { // 宿主设备定义了get_ts_info函数,则调用宿主设备提供的函数返回时间戳 return ops->get_ts_info(vlan->real_dev, info); } else { // 使用软件级提供的时间戳 info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE; info->phc_index = -1; } return 0; } // vlan设备管理操作回调函数集合 static const struct net_device_ops vlan_netdev_ops = { // 修改设备mtu .ndo_change_mtu = vlan_dev_change_mtu, // 提供网络设备的初始化、创建网络设备struct net_device数据结构实例、初始化struct net_device的相关数据域如设备名、i/o端口地址、中断号、向内核注册设备 .ndo_init = vlan_dev_init, // 注销设备 .ndo_uninit = vlan_dev_uninit, // 打开网络设备,注册的设备才能打开 .ndo_open = vlan_dev_open, // 停止网络设备,注销的时候调用和open相反 .ndo_stop = vlan_dev_stop, // 初始化数据包发送过程,初始化成功后数据包就放入网络适配器的发送缓冲区 .ndo_start_xmit = vlan_dev_hard_start_xmit, // 校验以太网地址 .ndo_validate_addr = eth_validate_addr, // 设置mac地址 .ndo_set_mac_address = vlan_dev_set_mac_address, // 设置设备对单播、组播的接收 .ndo_set_rx_mode = vlan_dev_set_rx_mode, // 修改rx flags,如开启混杂模式等 .ndo_change_rx_flags = vlan_dev_change_rx_flags, // 用户ioctl调用的函数 .ndo_eth_ioctl = vlan_dev_ioctl, // 初始化邻居子系统 .ndo_neigh_setup = vlan_dev_neigh_setup, // 当用户想要获取网络设备使用统计信息时调用 .ndo_get_stats64 = vlan_dev_get_stats64, #if IS_ENABLED(CONFIG_FCOE) .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup, .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done, .ndo_fcoe_enable = vlan_dev_fcoe_enable, .ndo_fcoe_disable = vlan_dev_fcoe_disable, .ndo_fcoe_ddp_target = vlan_dev_fcoe_ddp_target, #endif #ifdef NETDEV_FCOE_WWNN .ndo_fcoe_get_wwn = vlan_dev_fcoe_get_wwn, #endif #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = vlan_dev_poll_controller, .ndo_netpoll_setup = vlan_dev_netpoll_setup, .ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup, #endif .ndo_fix_features = vlan_dev_fix_features, .ndo_get_iflink = vlan_dev_get_iflink, .ndo_fill_forward_path = vlan_dev_fill_forward_path, }; static int vlan_dev_init(struct net_device *dev) { // 获取vlan设备私有信息 struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct net_device *real_dev = vlan->real_dev; // 通常网络设备会定时地检测设备是否处于可传递状态。当状态发生变化时,会调用netif_carrier_on或者netif_carrier_off来通知内核;从网上设备插拔网线或者另一端的设备关闭或禁止,都会导致连接状态改变;netif_carrier_on—-设备驱动监测到设备传递信号时调用,netif_carrier_off—-设备驱动监测到设备丢失信号时调用,上述两个状态改变函数均会调用linkwatch_fire_event将事件加入到事件队列进行调度 netif_carrier_off(dev); /* IFF_BROADCAST|IFF_MULTICAST; ??? */ dev->flags = real_dev->flags & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_MASTER | IFF_SLAVE); dev->state = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT))) | (1<<__LINK_STATE_PRESENT); // vlan设备是网桥成员 if (vlan->flags & VLAN_FLAG_BRIDGE_BINDING) dev->state |= (1 << __LINK_STATE_NOCARRIER); // 设置vlan设备硬件加速特性 dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL | NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC | NETIF_F_ALL_FCOE; dev->features |= dev->hw_features | NETIF_F_LLTX; netif_set_gso_max_size(dev, real_dev->gso_max_size); netif_set_gso_max_segs(dev, real_dev->gso_max_segs); if (dev->features & NETIF_F_VLAN_FEATURES) netdev_warn(real_dev, "VLAN features are set incorrectly. Q-in-Q configurations may not work correctly.\n"); dev->vlan_features = real_dev->vlan_features & ~NETIF_F_ALL_FCOE; dev->hw_enc_features = vlan_tnl_features(real_dev); dev->mpls_features = real_dev->mpls_features; /* ipv6 shared card related stuff */ dev->dev_id = real_dev->dev_id; // mac地址为0,即未指定mac if (is_zero_ether_addr(dev->dev_addr)) { // 传递宿主设备mac地址给vlan设备 eth_hw_addr_set(dev, real_dev->dev_addr); dev->addr_assign_type = NET_ADDR_STOLEN; } // 广播mac为0,则传递宿主广播mac给vlan设备 if (is_zero_ether_addr(dev->broadcast)) memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len); #if IS_ENABLED(CONFIG_FCOE) dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid; #endif dev->needed_headroom = real_dev->needed_headroom; // 检查宿主设备是否支持vlan硬件卸载加速 if (vlan_hw_offload_capable(real_dev->features, vlan->vlan_proto)) { // 硬件支持vlan卸载,则软件不用处理vlan头,直通(passthru)硬件处理即可 dev->header_ops = &vlan_passthru_header_ops; // 硬件直接添加二层头,不需要真实添加vlan头,预留二层以太网帧长度为14 dev->hard_header_len = real_dev->hard_header_len; } else { // 软件添加vlan头 dev->header_ops = &vlan_header_ops; // 由于软件添加了vlan头,预留二层以太网帧长度为18 dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN; } dev->netdev_ops = &vlan_netdev_ops; // 设置设备类型为vlan SET_NETDEV_DEVTYPE(dev, &vlan_type); // vlan网络设备下面有嵌套设备,是普通网络设备的特殊“超类”;将它们的锁拆分为一个单独的类,因为它们总是嵌套 vlan_dev_set_lockdep_class(dev); // 为每个cpu申请设备统计空间 vlan->vlan_pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats); if (!vlan->vlan_pcpu_stats) return -ENOMEM; // 宿主设备引用计数加1 /* Get vlan's reference to real_dev */ dev_hold_track(real_dev, &vlan->dev_tracker, GFP_KERNEL); return 0; } // struct header_ops包含以一系列函数指针,主要是对链路层协议头的操作,比如协议头的创建、解析、重建协议头。 static const struct header_ops vlan_header_ops = { // 协议头创建函数 .create = vlan_dev_hard_header, // 协议头解析函数 .parse = eth_header_parse, // 协议解析函数 .parse_protocol = vlan_parse_protocol, }; static const struct header_ops vlan_passthru_header_ops = { / 协议头创建函数 .create = vlan_passthru_hard_header, // 协议头解析函数 .parse = eth_header_parse, // 协议解析函数 .parse_protocol = vlan_parse_protocol, }; static int vlan_passthru_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, const void *saddr, unsigned int len) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct net_device *real_dev = vlan->real_dev; // 填充源MAC为vlan设备mac if (saddr == NULL) saddr = dev->dev_addr; // 发给宿主硬件设备填充vlan头和二层以太网头 return dev_hard_header(skb, real_dev, type, daddr, saddr, len); } /* * Create the VLAN header for an arbitrary protocol layer * * saddr=NULL means use device source address * daddr=NULL means leave destination address (eg unresolved arp) * * This is called when the SKB is moving down the stack towards the * physical devices. */ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, const void *saddr, unsigned int len) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct vlan_hdr *vhdr; unsigned int vhdrlen = 0; u16 vlan_tci = 0; int rc; if (!(vlan->flags & VLAN_FLAG_REORDER_HDR)) { // 添加vlan头 vhdr = skb_push(skb, VLAN_HLEN); // 填充vlan头字段 vlan_tci = vlan->vlan_id; vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority); vhdr->h_vlan_TCI = htons(vlan_tci); /* * Set the protocol type. For a packet of type ETH_P_802_3/2 we * put the length in here instead. */ if (type != ETH_P_802_3 && type != ETH_P_802_2) vhdr->h_vlan_encapsulated_proto = htons(type); else vhdr->h_vlan_encapsulated_proto = htons(len); // 设备skb以太网类型为vlan协议 skb->protocol = vlan->vlan_proto; // 真实三层协议 type = ntohs(vlan->vlan_proto); vhdrlen = VLAN_HLEN; } // 填充源MAC为vlan设备mac /* Before delegating work to the lower layer, enter our MAC-address */ if (saddr == NULL) saddr = dev->dev_addr; // 发给宿主硬件设备填充二层以太网头 /* Now make the underlying real hard header */ dev = vlan->real_dev; rc = dev_hard_header(skb, dev, type, daddr, saddr, len + vhdrlen); if (rc > 0) rc += vhdrlen; return rc; }


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

上一篇:PowerMockito的基本使用解析
下一篇:带你快速搞定java多线程(3)
相关文章

 发表评论

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