Linux内核vlan和vxlan源码阅读
vlan (802.1q)
通常的vlan是指IEEE 802.1q标准定义的virtual LANs, 就是在以太网头部插入一个vlan tag来划分子网的技术。 linux内核的vlan相关代码都在源码的linux-4.11\net\8021q 这个目录下面。
操作skb的vlan的辅助函数 vlan_insert_tag skb_vlan_untag skb_vlan_pop skb_vlan_push
接收
接收上来的包明显要经过vlan_do_receive函数来处理,用这个函数一搜索就看到包怎么从 协议栈层传上来了,但这个从网络协议栈上来的时候,之前就在__netif_receive_skb_core 里面做了untag处理了,这点最初没注意到找了好一会。
http://elixir.free-electrons.com/linux/latest/source/net/core/dev.c#L4152
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
skb = skb_vlan_untag(skb); // 读出vlan id,去掉 vlan tag,
if (unlikely(!skb))
goto out;
}
//中间处理各种注册的协议和抓包等。
if (skb_vlan_tag_present(skb)) {
if (pt_prev) {
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = NULL;
}
if (vlan_do_receive(&skb)) // 根据vlan id设置skb的dev 为vlan dev等
goto another_round; // 重新进入协议栈,再遍历一次各种注册的协议就会运行ip协议层函数处理了
else if (unlikely(!skb))
goto out;
}
}
从代码里面看调用这个vlan_do_receive 应该是在bridge的rx_handler hook点的前面、。
发送
发送很容易看,找vlan net device 对应的 ops 函数就可以了。 vlan_dev.c vlan_dev_hard_start_xmit
不相关的”vlan”
这个vlan完全和802.1q没有任何管理,就是能划分流量到多个interface上面去而已吧。 这里的介绍不错 http://hicu.be/macvlan-vs-ipvlan http://hicu.be/bridge-vs-macvlan
Macvlan (linux-4.11\drivers\net\macvlan.c)在一个物理网卡上面虚拟出支持多个 虚拟网卡(多个mac地址),看起来就像你的机器有几块网卡一样。 有点类似 bridge , 但应该理念不一样了。 docker都有支持macvlanx网络划分,把创建出来的设备分给不同虚拟机。 https://docs.docker.com/engine/userguide/networking/get-started-macvlan/
ipvlan (linux-4.11\drivers\net\ipvlan) https://github.com/torvalds/linux/blob/master/Documentation/networking/ipvlan.txt 跟 macvlan很类似的技术,但所有的接口都公用底层的一个mac。这个貌似跟设置 多个ip到一个接口上面不同的地方,就是ipvlan有多个interface。这样就可以 把创建出来的interface 分给不同的虚拟机来用吧。
https://en.wikipedia.org/wiki/IEEE_802.1Q
网上其他人写的linux内核的vlan相关的文章
http://www.cnblogs.com/zmkeil/archive/2013/04/18/3029339.htm
http://blog.csdn.net/bc_vnetwork/article/details/51787473
openvswitch的vlan
http://docs.openvswitch.org/en/latest/howto/vlan/ http://docs.openvswitch.org/en/latest/faq/vlan/
openvswitch的action 里面有pop_vlan push_vlan可以把网络包的vlan tag给修改了吧
ebtables可以过滤vlan tag的包,进行处理等?
ebtables -t broute -A BROUTING -i eth0 -p 802_1Q -j DROP
linux vlan的连接交换机的“vlan trunk mode”的文件
还有个一个不太理解的问题,因为linux要支持一个vlan的话 都会创建要给对应的虚拟net device,比如 eth0.100这种。 但如果eth0连的交换机的trunk接口的时候,要支持比较多的vlan id 比如4000个vlan, 难道要对应linux系统自己创建4000个vlan设备才行? 这种情形可能把这个linux作为nat网关,给交换机后面的多台设备用时可能 会碰到。如果按照常规的一个vlan id对应要给eth0.100虚拟设备这种方法 感觉太耗资源了。 想到一个办法,只能自己管理vlan id ,接收上来的可以自己用ovs或者ebtables这些 辅助删掉然后再按照所有包都是untaged的来处理。 但linux要回复的时候,又需要自己 把vlan tag给补回去。 这个自己写一个模块hook发送和接收的处理过程。感觉还是非常 麻烦的,需要自己维护vlan id和mac的映射表。 就有没有什么其他简便的方法,谁知道的话,可以告诉我。
vxlan
linux-4.11\drivers\net\vxlan.c
vxlan是vlan的改进,把二层的以太网包封装在udp层发送出去 这个的实现代码比vlan简单好多了,就是创建udp连接,udp收上来的包 调用 vxlan_rcv和 vxlan_gro_receive进行处理,然后调用gro_cells_receive和 eth_gro_receive 投递到内核协议栈。 发送更简单,vxlan_xmit函数里面封装成udp 就直接发送出去了。
static const struct net_device_ops vxlan_netdev_raw_ops = {
.ndo_init = vxlan_init,
.ndo_uninit = vxlan_uninit,
.ndo_open = vxlan_open,
.ndo_stop = vxlan_stop,
.ndo_start_xmit = vxlan_xmit,
.ndo_get_stats64 = ip_tunnel_get_stats64,
.ndo_change_mtu = vxlan_change_mtu,
.ndo_fill_metadata_dst = vxlan_fill_metadata_dst,
};