Linux VLAN透传

网友投稿 367 2022-10-14


Linux VLAN透传

一、 Linux对VLAN数据的处理(1)VLAN初始化static struct packet_type vlan_packet_type __read_mostly = {.type =cpu_to_be16(ETH_P_8021Q),.func = vlan_skb_recv, /*VLAN receive method */}; vlan_proto_init调用dev_add_pack(&vlan_packet_type)函数进行VLAN的注册,把ETH_P_8021Q 处理函数挂在ptype_base下,即数据接收的VLAN的入口函数为vlan_skb_recv (2)VLAN数据处理     内核中的vlan模块就是工作在协议栈里,通过注册dev_add_pack注册了8021Q的协议类型,netif_receive_skb->__netif_receive_skb遍历所有的协议类型时发现了它,于是进入了vlan的接收函数vlan_skb_recv(vlan_dev.c),这个函数里剥离vlan层,重置以太网层的proto,重新调用netif_rx进入真正的协议栈处理流程。对于Linux,无论什么数据包通过网卡驱动后都会进入__netif_receive_skb函数。该函数中对VLAN的处理如下:static int__netif_receive_skb(struct sk_buff *skb){     … …     //遍历ptye_all链表     list_for_each_entry_rcu(ptype, &ptype_all,list) {         if (ptype->dev == null_or_orig || ptype->dev ==skb->dev ||             ptype->dev ==orig_dev) {              if (pt_prev)                   ret = deliver_skb(skb, pt_prev, orig_dev);              pt_prev = ptype;         }     } … …     // bridge逻辑     /* Handle special case of bridge or macvlan */     rx_handler = rcu_dereference(skb->dev->rx_handler);     if (rx_handler) {         if (pt_prev) {              ret = deliver_skb(skb, pt_prev, orig_dev);              pt_prev = NULL;         }         skb = rx_handler(skb);         if (!skb)              goto out;     }     … …     //遍历ptype_base     type = skb->protocol;  //如果是VLAN,则这里type被置为VLAN协议,即0x8100(经过VLAN处理后,protocol会被重置)     list_for_each_entry_rcu(ptype,              &ptype_base[ntohs(type) & PTYPE_HASH_MASK],list) {         if (ptype->type == type && (ptype->dev ==null_or_orig ||              ptype->dev ==skb->dev || ptype->dev == orig_dev ||              ptype->dev ==orig_or_bond)) {              if (pt_prev){                   // deliver_skb函数最终调用paket_type.func(),如果是VLAN,由于type为802.1Q的协议,所以会调用其对应的协议处理函数:vlan_skb_recv                   ret = deliver_skb(skb, pt_prev, orig_dev);}              pt_prev = ptype;         }     }     … …} 二、 vlan_skb_recv函数解析定义几个vlan接口和vlan ID相关的宏:#define   MTC_STP_PACKET_VLAN_ID           4094#define   MTC_WAN_VLAN_ID                  2#define   MTC_LAN_VLAN_ID                  1#define   MTC_WAN_IFNAME                   "eth2.2"#define   MTC_LAN_IFNAME                   "eth2.1" int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,       struct packet_type *ptype, struct net_device*orig_dev){… … // 从skb解析出对应的vlan_idvhdr = (struct vlan_hdr*)skb->data;vlan_tci =ntohs(vhdr->h_vlan_TCI);vlan_id = vlan_tci &VLAN_VID_MASK;    … … //这里dev指向的是真实的dev,通过__find_vlan_dev函数根据vlan_id找到对应接口的dev(比如现在用的系统真实dev接口名称为eth2; vlan_id==1的接口名称为eth2.1; vlan_id==2的接口名称为eth2.2)vlan_dev =__find_vlan_dev(dev, vlan_id); //以下处理是为了确保vlan_id为非1和非2的时候数据包不会被丢掉#ifdef PRODUCT_MT7628STPAP_MTC/*There is not any device attachto the MTC_STP_PACKET_VLAN_ID(4094) vlan id,In order to pass the normalprocessing of vlan_skb_recv function,we set the vlan_dev toMTC_WAN_IFNAME(eth2.2).by wwk,20170426*/if(MTC_STP_PACKET_VLAN_ID ==vlan_id_back)         {                   vlan_dev = __dev_get_by_name(&init_net,MTC_LAN_IFNAME);}  #endif /* If the VLAN device isdefined, we use it. * If not, and the VID is 0, it is a 802.1ppacket (not * really a VLAN), so we will just netif_rx itlater to the * original interface, but with the skb->protoset to the * wrapped proto: we do nothing here. */if (!vlan_dev) {     if (vlan_id) {          pr_debug("%s:ERROR: No net_device for VID: %u on dev: %s\n",                __func__, vlan_id, dev->name);          //如果通过上面的函数__find_vlan_dev没找到vlan_dev,则把数据包扔掉,当前系统默认仅支持MTC_WAN_IFNAME和MTC_LAN_IFNAME,即VLAN ID为2和VLAN ID为1的VLAN          goto err_unlock;     }     else     {          … …}} else {     … …} //以下为对从WAN进来的包进行处理:从WAN进来的包copy一份同步发到LAN端,发送之前把把vlan_id设置为4096#ifdef PRODUCT_MT7628STPAP_MTC/*if pkg from vlanMTC_WAN_VLAN_ID, then forward one copy to MTC_LAN_IFNAME(vlan_id isMTC_STP_PACKET_VLAN_ID)add 4096vlan_tag-->vlan 4096-->send pkg from MTC_LAN_IFNAME interface>Must be called beforeskb_pull_rcsum() ,vlan_set_encap_proto() and vlan_check_reorder_header()by wwk,20170426*/… … //实现细节#endif //使用以下三个函数用来剥离VLAN层和重置以太网层的proto: skb_pull_rcsum、vlan_set_encap_proto、vlan_check_reorder_header skb_pull_rcsum(skb,VLAN_HLEN);vlan_set_encap_proto(skb,vhdr); if (vlan_dev) {     skb = vlan_check_reorder_header(skb);     if (!skb) {          rx_stats->rx_errors++;          goto err_unlock;     }} #ifdef PRODUCT_MT7628STPAP_MTCif(MTC_LAN_VLAN_ID ==vlan_id_back){              //  从MTC_LAN_IFNAME进来的数据包,视情况对VLAN进行处理     … … //实现细节} //if pkg from vlanMTC_STP_PACKET_VLAN_ID, then forward to MTC_WAN_IFNAME if(MTC_STP_PACKET_VLAN_ID == vlan_id_back){                   … … //实现细节     /*     There is not any deviceattach to the MTC_STP_PACKET_VLAN_ID(4094) vlan id,     so don't need netif_rx skbagain     */     goto err_unlock;}  #endif//netif_rx函数会让数据包重新进入__netif_receive_skb函数处理netif_rx(skb); rcu_read_unlock();return NET_RX_SUCCESS; err_unlock:rcu_read_unlock();err_free:kfree_skb(skb);return NET_RX_DROP;} 数据包进来后先修改为对应的vlan_id,然后直接通过dev_queue_xmit函数把数据包发出去,仅对对应的VLAN包进行转发,其他数据包按照路由器的处理机制处理。通过以上处理,实现VLAN数据的分流。


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

上一篇:一文搞懂单向散列加密:MD5、SHA-1、SHA-2、SHA-3
下一篇:SpingBoot中使用Redis对接口进行限流的实现
相关文章

 发表评论

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