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。