ipvlan是Linux提供的一个内核网络虚拟化的机制,最近在支持集团内某些业务的时候直接使用了ipvlan的网络方案,在解决问题和方案设计的时候也深入看了一下这块源码,看了一下并不复杂,索性写下来记录一下,以飨读者。
本文会省略一些具体的模拟环境的测试结论,因此更适合有ipvlan使用和调戏经验的同学阅读。
总论
ipvlan的使用体验实际上跟bridge是比较接近的,在配置好子网卡和IP地址后,就可以借助underlay网络互通了,在Linux Howto上有配置ipvlan的详细指引:
1 | bash复制代码 +=============================================================+ |
具体说来,每一个ipvlan网卡都会挂在一个父网卡上面,同一个父网卡下面挂的ipvlan默认是三层互通的(除非配置了private模式)并且使用相同的mac地址,而不同父网卡之间的子网卡则在ipvlan这一层相互隔离,从而形成了一种类似于vlan的隔离机制,因此取名叫做ipvlan。
但是这种隔离的关系仅仅是在Linux系统内部生效的,报文在出ipvlan子网卡之后会因ipvlan的各种配置和外部网络因素形成各种复杂的关系,本文就先从源码角度入手看一下ipvlan的内部实现,再配合外部网络常见的各种环境,对ipvlan可能的业务场景和技术方案做一些分析。
ipvlan基本的转发思路以每个父网卡作为一个隔离域配置一个hash表,每个子网卡在更新IP的时候把IP地址注册到这个hash表中,ipvlan再转发流程里面以这个ip-网卡之间的hash作为转发依据。
L2模式
对于L2模式而言,其与L3模式的主要区别在于这种模式下,还是保留了一些二层相关的流程到达ipvlan子网卡这里的,主要包括ARP流程和广播报文。
话不多说,我们可以先看下对于L2模式而言的关键转发逻辑:
1 | c复制代码static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev) |
兄弟网卡间的转发逻辑
这段代码比较清晰的描述了对于L2模式的转发逻辑,在不考虑vepa模式的情况下(这个模式会在4.1这个部分简单讲下),会首先判断一下报文的源mac和目的mac是否一致,如果一致则认为是ipvlan内部转发转流程,判断是否有三层头,有三层头自然就是带有IP层的报文,然后就会寻找挂在这个父网卡下的目的IP所在的子网卡,直接调用该网卡的ipvlan_rcv_frame函数,送到对应的子网卡下。
跨主机的转发逻辑
实际上对于ipvlan而言,还有一种重要场景就是跨linux之间的转发,对于这种情况,l2模式采用的方法是简单粗暴的:直接调用skb->dev = ipvlan->phy_dev; dev_queue_xmit(skb);通过父网卡直接送出去。
\
对于多播报文的处理
多播报文的处理其实也不需要太多关注,对于多播报文而言,只要ipvlan发现目的mac是一个多播地址,就会直接把他塞到backlog队列里去做转发。
一些衍生的问题
这里面有几个比较值得玩味的细节:
- 为什么源mac和目的mac地址一致就认为是ipvlan内部转发流程呢?
- 因为ipvlan这个神奇的模式,ipvlan子网卡会继承父网卡的mac地址,这也是他和macvlan模式的明显区别之一,所以兄弟网卡之间的mac地址必然是相同的。
- l2模式在跨主机转发的场景下是直接使用父网卡link的dev_queue_xmit发送的,bypass了所有的三四层的协议栈,这也就意味着ipvlan子网卡和父网卡在二层上是隔离的,和宿主机上的其他网卡更加是隔离的,这可能会给某些需要父子网卡互通的场景带来一些奇妙的麻烦,当然,你也不能指望L3模式帮你避开这个问题。
L3模式
对于L3模式而言,会给网卡做一些比较奇妙的配置,首先所有的多播和广播报文会在L3模式的网卡里面直接丢弃,等于多播完全在这种模式里面废除了;另外就是网卡会配置成noarp模式,不会响应和发出任何arp请求。那么就由小朋友要问了:那mac地址可怎么填呀?答案是对于子网卡而言,其发出的三层报文,源mac和目的mac都会设置成自己的mac地址。
简单过一下L3模式的转发代码:
1 | c复制代码static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev) |
兄弟网卡之间的转发逻辑
这个转发代码是不是有点过于简单?可以看出,兄弟网卡的判断逻辑跟L2是有所区别的,不能通过目的mac来判断了,因为不管是不是给兄弟发,目的mac都是一样的,那只能直接根据目的IP查表了,能查到就是兄弟的,直接调用兄弟的ipvlan_rcv_frame给他送过去,不用谢。
跨主机的转发逻辑
对于跨主机的转发, 和L2模式有显著的不同,不同点在于:
- 在outbound函数中增加了对mac_header的处理,对于目的MAC为多播的报文直接丢弃,然后把skbuf的mac_header直接清空,这一步应该是为了防止mac地址的配置影响路由子系统对发包的判断。
- 欸……等等,你刚才说了“路由子系统”对吧?嗯是的,可以看出,对于ipv4而言,发送的核心代码是ip_route_output_flow 和 ip_local_out ,这也就意味着,对于L3模式而言,报文的发送是经过了父网卡的路由子系统的,也就是最终网卡的发包是由父网卡路由子系统决定的下一跳……这是和L2模式的核心区别。
多播报文的处理
无论是收还是发,都是直接丢弃了事,就是这么残忍。
一些衍生的思考
- 在2.2中说到,在L2模式下父子网卡之间二层隔离,那这个问题在L3模式下是不是就不存在了?
- 是的,如果你直接在ipvlan子网卡ping父网卡,那父网卡是会在协议栈处理这个报文,并且能够回包的……但是你需要把子网卡的IP段设置在Host里面配置Link路由,报文才能正确地回到子网卡,这对于绝大多数容器网络应用来说并不是一个很明智的做法——不管你的host和容器是不是处于同一个网段。
关于那些flag
4.1 vepa这玩意
vepa这玩意是802.1Qbg提出的一种虚拟网络方案,其有独特的背景还是值得说一说的。
在古老的计算机网络体系里面,“交换”一般是指的基于MAC地址的二层转发,其对于未知主机的触达主要是依赖于“泛洪”,即对于未知目的MAC的报文,就很暴力地转发到全部物理端口,但是这个泛洪和转发,通常都是不包含这个报文的源物理端口的,原因嘛,也很简单,一个报文从这个口子发出来了,我再给他转发回去,万一下面也是个交换机,也给我原样转回来,这流量岂不是原地爆炸?不行不行。STP原理上也是不支持这样的转发模式。
但是随着云网络这玩意的兴起,交换机下面挂着的物理机也会有很多VM、容器之类的,有时候同一台物理机的VM之间互相通信,报文也直接怼给交换机了,这就是vepa模式。
那交换机转吗?转的话,那就开启一个hairpin模式,老老实实的转回去。
所以可以看出,vepa模式就是不管是不是同一物理机,我协议栈也不管,就是往外发,就是莽,交换机来给我处理好。所以这种模式通常是需要交换机配置来配合的,一般场景下,还真的用不到。
看ipvlan源代码也看出来,对于vepa模式,不管l2还是l3,都直接发出去了。
4.2 关于L3S和L3模式之间的差异
对于L3和L3s之间的差别,主要体现在接收流程上,2和3两节基本上没有讲接受逻辑,因为基本上乏善可陈,但是对于L3和L3s之间的差异,主要就体现在这个接收流程了,还是值得看一下的,关注一下rx_handle的相关逻辑:
1 | c复制代码rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb) |
可以看出来,l3模式的收包基本上走的还是查IP->确定子网卡->转发的老套路,而L3S却在ipvlan_handle_frame的流程里直接返回了RX_HANDLER_PASS,走了Host的协议栈,欸,这是几个意思?
不过可以看出,在L3S的网卡初始化过程中,ipvlan悄悄配置了几个回调进去:
1 | c复制代码static const struct l3mdev_ops ipvl_l3mdev_ops = { |
oh,他在netfilter的l3mdev_l3_rcv注册了一个ipvlan_l3_rcv的回调,结合上面的分析,l3s的收包流程会一路走到PREROUTING hook点,找到要转发的子网卡后,再通过子网卡的LOCAL_IN hook点到达。
这也就意味着,在ipvlan子网卡的PreRouting里面常做的一些配置是可能被L3S模式绕过的,比如nat。目前我还没有遇到过需要使用L3S模式的应用场景。
4.3 关于private
private主要用于隔离兄弟网卡之间的报文转发。对于一些只提供南北向流量服务,又对隔离性有需求的应用可以使用private模式。
- 总结一下
通过上面的代码分析,可以看出ipvlan这玩意,提供了最基本的转发和隔离能力,并且代码这块短小精悍,省略了很多麻烦的处理流程,我们实际测试下来性能也相当可观。其基于网卡粒度的隔离思路,配合vlan子网卡这个模式可以在IDC配合交换机配置内轻松地实现vlan隔离。其与三层转发强关联、子网卡共享mac地址的方式也更适合带有三层交换网关的业务隔离系统,与使用去堆叠架构或者云上网络的纯三层转发系统相性非常好,是云上容器网络的一个绝佳方案。
但是可以看出ipvlan是一个underlay的解决方案,因此也需要上联的云网络或者物理网络的配置配合,考虑到物理网络更新的复杂性和风险,其实更适合做云上容器网络的解决方案。
本文转载自: 掘金