Linux VLAN框架及其在Intel网卡I350的实现

一、 科普:什么是VLAN

常提到的VLAN是依据IEEE802.1Q标准定义的,其最核心的概念是所谓的TAG。我们经常提到的TAG,本质上是VLAN字段的VID字段。下面贴出VLAN的字段含义:


从定义可以看出,VLAN是一种特殊的以太网包定义,这就意味着VLAN是一种二层的概念。本质上VLAN数据包就是以太网类型为0x8100的以太网数据包,VLAN的提出是为了在一个实体广播域内划分出更多的广播域,实现子网的隔离。如果不同的VLAN子域之间需要互相通信,就需要三层交换的介入。

二、 Linux的VLAN支持

Linux内核中有一个“802.1Q VLAN Support”的网络选项,可以模块或者built-in到内核镜像提供对VLAN的内核支持。内核态的实现是无法跟用户交互的,在Linux系统使用VLAN功能还需要有vconifg这个用户态工具

 vconfig --help


Create and remove virtual ethernet devices


add IFACE VLAN_ID
rem VLAN_NAME
set_flag IFACE 0|1 VLAN_QOS
set_egress_mapVLAN_NAME SKB_PRIO VLAN_QOS
set_ingress_mapVLAN_NAME SKB_PRIO VLAN_QOS
set_name_type NAME_TYPE


比较常用到的命令是add:在某个以太网口添加换一个VLAN 端口,rem删除一个已经添加的VLAN端口。

关于如何用vconfig实现类似交换机的VLAN交换功能,网络上有很多博文做了描述,这里就不交代了。

三、Intel网卡I350对Linux VLAN的支持

内核主线的VLAN处理与网卡的接口部分变动很大,下面分别以2.6.28和3.8.0两个内核版本做分别介绍

首先说一下I350对VLAN的支持:I350的网卡驱动接收和发送部分的skb普通的skb数据包,skb->data里面是没有任何VLAN的信息的。I350硬件处理VLAN的接收和发送。

接收时I350会对接收到的VLAN数据包做去tag的处理,然后把处理后的数据包上传到linux网络协议栈。

发送时Linux协议栈发送来的数据包也是untagged,发送的时候网卡硬件会做相应的TAG处理。

硬件是如何做到接收和发送的处理的呢,当然软件不可能什么工作都不做,就让网卡完成VLAN的处理。 I350的网卡有个核心的概念就是:发送/接收描述符

接收描述符回写时定义


网卡在接收到VLAN数据包时,会根据系统的配置,决定是否接收该VLAN数据包到协议栈,如果符合接收的条件,会把数据包的VLAN信息记录到描述符

发送描述符


发送时,网卡驱动会把协议栈记录的相应的VLAN信息填写到发送描述符指导网卡的硬件做出正确的处理。

下面看一下不同版本的内核是如何与网卡驱动配合实现对VLAN的支持的。

首先看一下用于处理特殊接收信息的服务代码igb_process_skb_fields的片段

f ((dev->features & NETIF_F_HW_VLAN_RX) &&
	    igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) {
		u16 vid = 0;
		if (igb_test_staterr(rx_desc, E1000_RXDEXT_STATERR_LB) &&
		    test_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &rx_ring->flags))
			vid = be16_to_cpu(rx_desc->wb.upper.vlan);
		else
			vid = le16_to_cpu(rx_desc->wb.upper.vlan);
#ifdef HAVE_VLAN_RX_REGISTER
		IGB_CB(skb)->vid = vid;
	} else {
		IGB_CB(skb)->vid = 0;
#else
		__vlan_hwaccel_put_tag(skb, vid);
#endif
发接收描述符获取了VLAN的VID信息之后如何记录以来与宏HAVE_VLAN_RX_REGISTER

这个宏是如何定义的呢,在驱动头文件里有这样的片段

#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,4,18) )
#ifndef HAVE_VLAN_RX_REGISTER
#define HAVE_VLAN_RX_REGISTER
#endif
#endif /* > 2.4.18 */
#endif /* < 2.6.37 */
在低版本的内核下,网卡驱动把VID信息记录到自己定义的数据结构GB_CB(skb)->vid

高版本的内核,通过__vlan_hwaccel_put_tag直接记录到skb->vid域

/**
 * __vlan_hwaccel_put_tag - hardware accelerated VLAN inserting
 * @skb: skbuff to tag
 * @vlan_tci: VLAN TCI to insert
 *
 * Puts the VLAN TCI in @skb->vlan_tci and lets the device do the rest
 */
static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb,
						     u16 vlan_tci)
{
	skb->vlan_tci = VLAN_TAG_PRESENT | vlan_tci;
	return skb;
}

高版本的内核处理时会占用VLAN的一个优先级的bit位。这个bit如何处理不好在华为的S3300交换机会出现同一个VID的数据包,VLAN域内不能转发的问题。

网卡驱动获取了相应的vid的信息后,将数据包提交到内核协议栈在高低版本也是有区别的

#ifdef HAVE_VLAN_RX_REGISTER
			igb_receive_skb(q_vector, skb);
#else
			napi_gro_receive(&q_vector->napi, skb);
#endif
在低版本的内核中需要驱动自己实现一个服务来兼容VLAN数据和非VLAN数据的接收,就是igb_receive_skb

#ifdef HAVE_VLAN_RX_REGISTER
/**
 * igb_receive_skb - helper function to handle rx indications
 * @q_vector: structure containing interrupt and ring information
 * @skb: packet to send up
 **/
static void igb_receive_skb(struct igb_q_vector *q_vector,
                            struct sk_buff *skb)
{
	struct vlan_group **vlgrp = netdev_priv(skb->dev);

	if (IGB_CB(skb)->vid) {
		if (*vlgrp) {
			vlan_gro_receive(&q_vector->napi, *vlgrp,
					 IGB_CB(skb)->vid, skb);
		} else {
			dev_kfree_skb_any(skb);
		}
	} else {
		napi_gro_receive(&q_vector->napi, skb);
	}
}

#endif /* HAVE_VLAN_RX_REGISTER */
napi_gro_receive实际是就是内核协议栈与网卡接收的接口函数netif_receive_skb

#define napi_gro_receive(_napi, _skb) netif_receive_skb(_skb)

发送方向上的处理是通过igb_xmit_frame_ring来实现

if (vlan_tx_tag_present(skb)) {
		tx_flags |= IGB_TX_FLAGS_VLAN;
		tx_flags |= (vlan_tx_tag_get(skb) << IGB_TX_FLAGS_VLAN_SHIFT);
	}

	/* record initial flags and protocol */
	first->tx_flags = tx_flags;
	first->protocol = protocol;

	tso = igb_tso(tx_ring, first, &hdr_len);
	if (tso < 0)
		goto out_drop;
	else if (!tso)
		igb_tx_csum(tx_ring, first);
获取skb的vid信息后,通过igb_tx_sum写入到发送描述符,下面是程序片段

		/* update TX checksum flag */
		first->tx_flags |= IGB_TX_FLAGS_CSUM;
	}

	vlan_macip_lens |= skb_network_offset(skb) << E1000_ADVTXD_MACLEN_SHIFT;
	vlan_macip_lens |= first->tx_flags & IGB_TX_FLAGS_VLAN_MASK;

	igb_tx_ctxtdesc(tx_ring, vlan_macip_lens, type_tucmd, mss_l4len_idx);
}






  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值