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
第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 }
内核模块捕获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", }, };
如果vconfig的方式创建了VLAN设备,那么不需要对skb->data进行4字节的偏移就可以捕获到想要的数据包。
参考资料
【0】LINUX内核精髓(精通LINUX内核必会的75个技巧)
【1】计算机网络 第5版 严伟,潘爱民译