- 添加vlan子接口
可以通过vconfig或者ip命令添加vlan子接口,命令如下:
vconfig add eth0 10
或者
ip link add link eth0 name vlan10 type vlan id 10
执行上面的vconfig命令时,在kernel中代码流程如下,通过ip link设置vlan子接口走的是rtnl_link这一套流程,这里暂时先不分析了。
vlan_ioctl_handler
//添加vlan子接口
ADD_VLAN_CMD: register_vlan_device
//删除vlan子接口
DEL_VLAN_CMD: unregister_vlan_dev
在register_vlan_device中:
a. 调用alloc_netdev分配new_dev结构体
struct net_device *new_dev;
new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name,
NET_NAME_UNKNOWN, vlan_setup);
//在vlan_setup中,赋值如下
dev->netdev_ops = &vlan_netdev_ops;
dev->ethtool_ops = &vlan_ethtool_ops;
b. 分配vlan私有结构体
struct vlan_dev_priv *vlan;
vlan = vlan_dev_priv(new_dev);
vlan->vlan_proto = htons(ETH_P_8021Q);
vlan->vlan_id = vlan_id;
vlan->real_dev = real_dev;
vlan->dent = NULL;
vlan->flags = VLAN_FLAG_REORDER_HDR;
new_dev->rtnl_link_ops = &vlan_link_ops;
c. 注册vlan子接口register_vlan_dev(new_dev)
register_netdevice(dev);->list_netdevice(dev);
/* Device list insertion */
static void list_netdevice(struct net_device *dev)
{
struct net *net = dev_net(dev);
write_lock_bh(&dev_base_lock);
list_add_tail_rcu(&dev->dev_list, &net->dev_base_head);
hlist_add_head_rcu(&dev->name_hlist, dev_name_hash(net, dev->name));
hlist_add_head_rcu(&dev->index_hlist, dev_index_hash(net, dev->ifindex));
write_unlock_bh(&dev_base_lock);
dev_base_seq_inc(net);
}
在一个net网络空间中,不管物理网卡还是虚拟网卡,每次调用register_netdevice都会将此dev同时添加到此net空间中的三个链表中: net->dev_base_head, net->dev_name_head, net->dev_index_head.
我们通过ifconfig或者ip addr show命令时,都会通过这三个链表其中之一获取到net空间中的所有接口。
Linux下的网络设备net_dev并不一定都对应实际的硬件设备,只要注册一个struct net_device{}结构体(netdevice.h)到内核中,那么这个网络设备就存在了。
- vlan子接口发包流程
调用vlan_dev_hard_start_xmit
a.
//如果mac头的协议类型不是vlan协议,说明还没有打上vlan
//tag,需要添加vlan tag。
//但此时不会直接添加,而是设置标志位,让物理网卡发送函
//数完成vlan添加的操作。
if (veth->h_vlan_proto != vlan->vlan_proto ||
vlan->flags & VLAN_FLAG_REORDER_HDR) {
u16 vlan_tci;
vlan_tci = vlan->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority);
skb = __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);
}
//比如在ixgbe的发送函数中ixgbe_xmit_frame_ring会进行判
//断,如果需要添加vlan,则设置相应标志,让网卡完成vlan
//的添加
/* if we have a HW VLAN tag being added default to the HW one */
if (vlan_tx_tag_present(skb)) {
tx_flags |= vlan_tx_tag_get(skb) << IXGBE_TX_FLAGS_VLAN_SHIFT;
tx_flags |= IXGBE_TX_FLAGS_HW_VLAN;
b.
//将skb中的dev换成物理网卡的dev
skb->dev = vlan->real_dev;
c.
//调用物理网卡的xmit函数发送出去
dev_queue_xmit(skb);
3.vlan的接收流程可参考neiif_receive_skb部分
注册vlan子接口时,会将创建的vlan子接口的net_device和其vid保存到父接口的net_device的vlan_info中,父接口收到报文时,如果报文携带vlan,则会查找其vlan_info确定是否有相关的vlan子接口,如果有,则将skb->dev换成vlan子接口的net_device,重新走一遍协议栈(neiif_receive_skb)。