1 VLAN在linux中的实现
1.1 VLAN代码的位置
Vlan在linux的代码中位于 linux2.6.10/net/8021q,主要包含vlan.c
Vlan_dev.c :vlan设备相关的处理函数,比如设备上的报文发送、接收与报文头处理函数 vlan_dev_hard_start_xmit,vlan_skb_recv,vlan_dev_rebuild_header;设备的ioctl配置vlan_dev_ioctl函数等。
Vlan.c:vlan模块初始化,vlan设备的注册register_vlan_device、注销unregister_vlan_device,ioctl入口函数vlan_ioctl_handler等。
Vlanproc.c:提供proc文件系统的访问接口。
另外在linux/include里面还有一个if_vlan.h文件,提供了vlan对外的必要接口。
1.2 vlan数据的收发
在接收中断的下半部(下半部的主要工作就是遍历有数据帧等待接收的数据链表,对于每个设备执行它对应的poll函数)的处理函数netif_receive_skb中,对报文的处理做了实现。
int netif_receive_skb(struct sk_buff *skb)
{
struct packet_type *ptype, *pt_prev;
//进行sniffer等协议的报文上报
list_for_each_entry_rcu(ptype, &ptype_all, list) {
if (!ptype->dev || ptype->dev == skb->dev) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev);
pt_prev = ptype;
}
}
//进行数据帧的桥处理:如果是vlan报文直接退出
if (handle_bridge(&skb, &pt_prev, &ret))
goto out;
//进行vlan协议的处理
type = skb->protocol;
list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) {
if (ptype->type == type &&
(!ptype->dev || ptype->dev == skb->dev)) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev);
pt_prev = ptype;
}
}
}
而deliver函数的处理为
static __inline__ int deliver_skb(struct sk_buff *skb,
struct packet_type *pt_prev)
{
atomic_inc(&skb->users);
return pt_prev->func(skb, skb->dev, pt_prev);
}
对于VLAN报文就是static struct packet_type vlan_packet_type = {
.type = __constant_htons(ETH_P_8021Q),
.func = vlan_skb_recv, /* VLAN receive method */
};
上文中提到的一个重要的数据结构就是 struct packet_type
struct packet_type {
unsigned short type; /* This is really htons(ether_type). */
struct net_device *dev; /* NULL is wildcarded here */
int (*func) (struct sk_buff *, struct net_device *,
struct packet_type *);
void *af_packet_priv;
struct list_head list;
};
1.3 Vlan的初始化过程
在模块初始化vlan_proto_init时执行了dev_add_pack(&vlan_packet_type);将vlan协议的报文类型变量vlan_packet_type添加到ptype_base数组里面去,当桥处理执行时发现报文需要继续处理,那么就进入了vlan_skb_recv函数。
在这个函数中,首先根据数据帧的VLANID和实设备构造出一个虚设备,然后将报文中的tag去掉,将这个skb重新挂入skb队列,当系统在此调度的时候,进入桥代码之后就是一个没有tag的报文,就可以进行桥代码的处理了。
在进行vlan数据报文发送时比较简单。由于发送时指定的设备为需设备,这个时候该设备的hard_start_xmit就是 vlan_dev_hard_start_xmit。