这次先看看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 */
};
->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标签已解析,内封的协议类型被更新,这次能够走进新的协议类型处理流程中去了