使用vconfig或者ip命令创建的802.1q虚拟设备,
并不会负责vlan标签的剥离和添加。其操作位于数据包的接收和发送流程中。
vconfig add eth0 10
或者
ip link add link eth0 eth0.10 type vlan id 10
vconfig add eth0 10
或者
ip link add link eth0 eth0.10 type vlan id 10
vlan标签的剥离是在数据包接收函数__netif_receive_skb_core中处理,skb_vlan_untag函数最终执行untag标签剥离操作。首先将vlan协议类型(ETH_P_8021Q/ETH_P_8021AD)保存在skb的vlan_proto中,将vlan_tci(标签控制信息)保存在skb的vlan_tci中,之后将skb的data跳过VLAN信息字段(VLAN_HLEN - 4字节),即进行vlan标签剥离。
static inline void __vlan_hwaccel_put_tag(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) { skb->vlan_proto = vlan_proto; skb->vlan_tci = VLAN_TAG_PRESENT | vlan_tci; } struct sk_buff *skb_vlan_untag(struct sk_buff *skb) { vhdr = (struct vlan_hdr *)skb->data; vlan_tci = ntohs(vhdr->h_vlan_TCI); __vlan_hwaccel_put_tag(skb, skb->protocol, vlan_tci); skb_pull_rcsum(skb, VLAN_HLEN); }
数据包发送过程中,__dev_queue_xmit函数在执行发送操作的时候调用validate_xmit_skb添加vlan标签。sch_direct_xmit函数同样会调用validate_xmit_skb验证函数,但是由于vlan设备在创建的时候IFF_NO_QUEUE标志,不会进入流控系统,所以vlan数据包最终是由__dev_queue_xmit直接发送。__vlan_hwaccel_push_inside函数将vlan标签插入到数据包中。
void vlan_setup(struct net_device *dev) { dev->priv_flags |= IFF_802_1Q_VLAN | IFF_NO_QUEUE; } static inline struct sk_buff *__vlan_hwaccel_push_inside(struct sk_buff *skb) { skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto, skb_vlan_tag_get(skb)); } static struct sk_buff *validate_xmit_vlan(...) { skb = __vlan_hwaccel_push_inside(skb); } static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev) { skb = validate_xmit_vlan(skb, features); }
内核版本 linux-4.14.4