vlan 介绍

简介
     在Linux中安装了802.1Q标签VLAN功能。VLAN是虚拟分配以太网的功能。
使用VLAN ID从物理上将一个以太网分割开。在VLAN环境下,具有相同VLAN ID
就可以相互通信,但是即使将LAN线连接到相同集线器或交换机上,VLAN ID不同
也不能相互通信。
 
802.1Q的以太帧格式
 
由上图,唯一的变化是加入了一对2字节字段。第一个2字节是VLAN协议标识符,
它的值总是0x8100.由于这个数值大于1500,因此,所有的以太网卡都不会将
它解释成类型(type),而不是长度。第二个2字节包含三个子字段。最主要的是
VLAN标识符字段,它占用低12位。优先级和CFI的作用不明显,具体可以参考【1】的第4.8.5一节。
 
vlan在网卡驱动及协议栈的处理
     以igb网卡为例,从napi的poll函数开始调用情况如下:
igb_poll
     ==》
          igb_clean_rx_irq
          ==》
               igb_receive_skb
             
在igb_clean_rx_irq实现中:
5988
5989         if (igb_test_staterr(rx_desc, E1000_RXDEXT_STATERR_LB) &&
5990             test_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &rx_ring->flags))
5991             vlan_tag = be16_to_cpu(rx_desc->wb.upper.vlan);
5992         else
5993             vlan_tag = le16_to_cpu(rx_desc->wb.upper.vlan);
5994
5995         total_bytes += skb->len;
5996         total_packets++;
5997
5998         skb->protocol = eth_type_trans(skb, rx_ring->netdev);
5999
6000         igb_receive_skb(q_vector, skb, vlan_tag);
6001
View Code

第5989到5993行会读取vlan_tag。

函数igb_receive_skb的实现如下:

5829 static void igb_receive_skb(struct igb_q_vector *q_vector,
5830                             struct sk_buff *skb,
5831                             u16 vlan_tag)
5832 {
5833     struct igb_adapter *adapter = q_vector->adapter;
5834
5835     if (vlan_tag && adapter->vlgrp)
5836         vlan_gro_receive(&q_vector->napi, adapter->vlgrp,
5837                          vlan_tag, skb);
5838     else
5839         napi_gro_receive(&q_vector->napi, skb);
5840 }
View Code
内核模块捕获VLAN
     通过 vlan_gro_receive 和  napi_gro_receive的数据帧是有区别的。当我们使用命令
vconfig add eth1 22 创建vlan设备后,数据帧将会通过vlan_gro_receive,否则将会通过
napi_gro_receive到达netif_receive_skb。
     对于依然带有VLAN信息的数据包,捕获相应的数据包可采用jprobe的方法,按如下实现:
int jpf_netif_receive_skb(struct sk_buff *skb)
{
        unsigned short sport, dport;
        __be32 saddr, daddr;
        char dsthost[16];
        struct iphdr *iph;
        struct tcphdr *tcph;

        skb_linearize(skb);
        iph = (struct iphdr *)(skb->data + 4);
        tcph = (struct tcphdr *)((skb->data + 4) + (iph->ihl << 2));

        if (iph->protocol == IPPROTO_TCP) {
                sport = tcph->source;
                dport = tcph->dest;
                saddr = iph->saddr;
                daddr = iph->daddr;

               snprintf(dsthost, 16, "%pI4", &daddr);
               printk(KERN_INFO "ip is:%s", dsthost);
        }
        jprobe_return();
        return 0;
}
 
struct jprobe jps_netif_receive_skb = {
    .entry = jpf_netif_receive_skb,
    .kp = {
        .symbol_name = "netif_receive_skb",
    }, 
};
View Code
如果vconfig的方式创建了VLAN设备,那么不需要对skb->data进行4字节的偏移就可以捕获到想要的数据包。
 
参考资料
【0】LINUX内核精髓(精通LINUX内核必会的75个技巧)
【1】计算机网络 第5版 严伟,潘爱民译
posted @ 2015-10-20 17:33  lxgeek  阅读(1529)  评论(1编辑  收藏  举报