linux网络协议栈分析笔记7-VLAN的处理

这次先看看VLAN怎么处理的        主要代码目录linux/net/8021q
dev_add_pack(& vlan_packet_type);         vlan-802.1q
static struct packet_type vlan_packet_type __read_mostly = {
     .type = cpu_to_be16(ETH_P_8021Q),
       .func = vlan_skb_recv,  /* VLAN receive method */
};
->vlan_skb_recv
     ->skb = skb_share_check(skb, GFP_ATOMIC);     检查是否有其他协议共享处理此skb
     ->vhdr = (struct   vlan_hdr  *)skb->data;      指向VLAN tag字段
        vlan_tci = ntohs(vhdr->h_vlan_TCI);
        vlan_id = vlan_tci & VLAN_VID_MASK;

     struct   vlan_hdr  {
     __be16     h_vlan_TCI;             下图中的Tag Control Info      
     __be16     h_vlan_encapsulated_proto;   下图中的Len/Etype
};
这里关键之处得到vlan_id    
skb->dev = __find_vlan_dev(dev, vlan_id);  通过dev 与 vlan_id获得虚拟vlan dev  重置skb的dev变量
__find_vlan_dev()
     ->struct vlan_group *grp = __vlan_find_group(real_dev);            寻找vlan group

struct vlan_group {
     struct net_device     *real_dev; /* The ethernet(like) device         指向真实的dev
                             * the vlan is attached to.
                             */
     unsigned int          nr_vlans;                              
     struct hlist_node     hlist;     /* linked list */
     struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS];           vlan设备的管理空间
     struct rcu_head          rcu;
};



static struct vlan_group * __vlan_find_group(struct net_device *real_dev)
{
     struct vlan_group *grp;
     struct hlist_node *n;
     int hash = vlan_grp_hashfn(real_dev->ifindex);                 //return ((idx >> VLAN_GRP_HASH_SHIFT) ^ idx) & VLAN_GRP_HASH_MASK;
                                                                                            简单的hash函数,用设备接口索引去hash
     hlist_for_each_entry_rcu(grp, n, &vlan_group_hash[hash], hlist) {
          if (grp->real_dev == real_dev)        用真实的dev变量去匹配
               return grp;
     }

     return NULL;
}
摘自网上资料  这个说的很清楚:
数据结构vlan_group_hash是vlan虚拟网卡存储与关联的核心结构:
static struct hlist_head vlan_group_hash[VLAN_GRP_HASH_SIZE]; [net\8021q\vlan.c]
当通过vconfig创建了eth1.1, eth1.2, eth1.100三个虚拟网卡后,vlan_group_hash的整体结构如图所示,先有个整体印象: 
vlan_group_hash是大小为32的hash表

而vlan group是合适创建的呢
在添加vlan时,会创建新的vlan虚拟网卡:
        register_vlan_device() -> register_vlan_dev()
        首先查找网卡是否已存在,这里的real_dev一般是真实的网卡如eth1等。以real_dev->ifindex值作hash,取出vlan_group_hash的表项,由于可能存在多个网卡的hash值相同,因此还要匹配表项的real_dev是否与real_dev相同。
如果不存在相应的表项,则分配表项struct vlan_group,并加入vlan_group_hash
ngrp = grp = vlan_group_alloc(real_dev);   最后,设置vlan_devices_arrays相应元素指向创建的vlan虚拟网卡(如eth1.1)的struct net_device。这里值得注意的是vlan_devices_arrays是二维数组,内核支持的最大vlan数是4096,为了查找效率,应用了二级目录的概念。vlan_devices_arrays指向大小512的数组,数组中每个再指向大小8的数组,像eth1.100则位于第12组的第5个(vlan_devices_arrays[11][4])。
vlan_group_set_device(grp, vlan_id, dev);   以一个例子来说明,当主机收到报文,交由vlan协议模块处理后(vlan_rcv),此时需要更换skb->dev所指向的设备,以使上层协议认为报文是来自于虚拟网卡(比如eth1.1),而不知道网卡eth1的存在。更换设备就需要知道skb->dev更换的目标。这由两个因素决定:skb->dev和vlan_id。skb->dev即报文来自主机的哪个网卡,如来自eth1,则skb->dev->name=”eth1”;vlan_id即vlan号,这在报文中的vlan报文中可以提取出。有了这两个信息,从vlan_group_hash出发,首先根据skb->dev->ifindex查找vlan_group_hash的相应项(eth1),取出vlan_group;然后,根据vlan_id,在vlan_devices_array中查找到虚拟网卡设备(eth1.1)。
一般支持的最大vlan数是4096,为了查询效率,vlan_devices_array并不是一个4096的数组,而是二维数组,将每8个vlan分为一组,共512组,像eth1.100则位于第12组的第5个。
接着分析vlan_skb_recv()
     ->skb_pull_rcsum(skb, VLAN_HLEN);   跳过VLAN标签
     ->skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci);  设置TCI
     ->vlan_set_encap_proto(skb, vhdr);  设置新的协议类型  从vlan标签解出

     -> netif_rx(skb);  又见 netif_rx ,没错, 此时VLAN标签已解析,内封的协议类型被更新,这次能够走进新的协议类型处理流程中去了

你可能感兴趣的:(linux网络协议栈分析笔记7-VLAN的处理)