引言 OpenvSwitch(以下简称OVS)的代码,从软件架构上来讲还是很清晰的。但是笔者在工作中,了解到很多同学感觉,OVS的代码非常难懂,和传统的网络处理代码很不一样,难以理解。社区也没有文档对其内部的代码进行分析。本文试图将这个理解上的鸿沟填平,将OVS设计中的隐藏原则揭示出来。快慢路径与转发等价类(ForwardingEquivalenceClass)转发等价类 我们先从传统包处理软件,比如NAT和LoadBalancer的架构说起。这两类包处理软件都属于有状态(stateful)软件。他们的基本架构,一般是包含了慢速路径(slowpath),和快速路径(fastpath)的两个部分。快速路径本质上是利用数据包五元组(指数据包的源目的IP、源目的端口号、协议号)缓存进行数据包的快速查找,慢速路径则一般是包含各种查找表,比如路由表、ACL表的查找等。处理流程一般是首包MISS快速路径之后,会一次经历慢速路径的各种查找,并最终生成五元组插入到快速路径。下面这个图,我相信很多有Stateful网关开发经验同学已经看过了无数次了。 有的时候出发的太久就忘了了为什么出发。我们回想一下,为什么快速路径一定是五元组哈希表呢?本质上NAT和LoadBalancer处理的都是传输层的流量,TCPUDP是一个端到端的协议,其基本单位就是一条流(五元组)。为什么快速路径是五元组,本质原因是,对于这类网关而言,五元组是一个转发等价类(ForwardingEquivalenceClass)。这里借用了MPLS的术语,意思就是说,在这类网关上,如果一个数据包的五元组相同,那么他们一定有着相同的处理转发动作。 那么,是否所有的网关的转发等价类都是五元组呢?答案是否定的。比如一个路由器,他所关心的,仅仅是数据包的目的IP地址,那么他的快慢路径就可以是以下这个图。 由此可见,fastpath缓存的是转发等价类(FEC),只要命中一条FEC,包就自动满足了slowpath多个表的查找结果,也就可以直接执行转发动作了。FEC是从多个表计算出来的,同时,当这些表的规则发生变更时,FEC也会跟着发生变化。也就是,快慢路径这种设计的本质,就是计算出转发等价类,便利底层的查找。 这里引出了一个问题,也就是当规则发生变化的时候,我们如何保证fastpath所缓存的转发等价类依旧符合slowpath的转发语义呢?这里有一个参考设计,即引入一个Revalidator。Revalidator Revaldiator,顾名思义就是重新validatefastpath的FEC是否符合slowpath的转发语义。怎么做呢,很简单。 首先,把触发生成每个FEC的原始数据包记录下来,比如路由器,记录下触发生成当前FEC的IP地址记录下来。 然后,当规则发生变化时,Revalidator将这个数据包,重新计算一遍FEC,和当前的FEC对比,如果一样,就保持不变,如果发生变化,则删掉以前的FEC,新增一个新的FEC。 Revalidator一般是一个独立于slowpath和fastpath的单独线程,因此,当规则发生变化时,Revalidator会并行的更新fastpath的FEC记录。 因为Revalidator是一个独立的线程,能够周期性的做一些事情,因此能扩展出除了计算FEC以外的其他任务。 看到这里,有的同学可能会疑惑,为何NAT和LoadBalancer里没见到这个Revalidator?因为,他们不需要。连接跟踪和五元组查找 我们在上面的描述中,为了简便,把连接跟踪(connectiontracking)和转发等价类的缓存的概念混淆了。在有状态网关软件中,快速路径并不仅仅是五元组哈希表查找,而是连接跟踪。从查找的角度上看,这两者是等价的,本质上,两者的转发等价类都是五元组。而实际上,当规则发生变化时,LoadBalancer和NAT不能立马改变已有流的转发动作,这个原因还是因为,网络中的流是符合端到端原则的,建立起来的流,必须等待流的生命期终止才能改变。试想,如果从一个LB中摘掉一个RS(RealServer),LB是不能将已有的TCP连接流量直接导入到其他RS的,因为就算你导入了,那些TCP连接也是建立在这个摘掉的RS上的,而不会是其他的RS上的。 如果我们关注的是流的生命周期,那么已经建立的流的转发动作是不能变的。如果我们是从转发等价类的角度看,当规则发生变化,底层的缓存应该跟着变化,以符合slowpath的转发语义。交换机配置、规则的包处理语义 讨论完了转发等价类,我们再来看看视角。我经常感觉,网络处理有两类视角,一个是网络工程师的视角,一类是软件工程师视角。 在网络工程师的视角里,包处理和Interface(网口)的概念息息相关。他关注的是Interface是一个二层口还是三层口,一组口在哪一个域里,是一个二层域(bridgedomain),还是一个三层域(VirtualRoutingandForwarding,VRForRoutingtables)。 在软件工程师的视角里,数据包从一堆接口上收到,查找了MAC表,IP表,对端MAC表之后,转发到另一堆接口里。从某种程度上讲,网络工程师使用的declarativelanguage(声明式语言)来表示网络处理,而软件工程师的视角,是一个Imperativeprogramming(指令式编程)的视角。 严格意义上讲,一个二层域并不仅仅代表数据包处理需要经过MAC表查找,他还隐式声明了,这个二层域内的接口上的数据包都需要进行MACLearning(MAC地址学习)。OVS设计原则一:把一切都等价为MatchActionRevalidation 终于到了要讲OVS的架构了,现在再看OVS的架构,一切都是水到渠成的自然。 OVS的架构大体上可以看成是三个部分:1)ofproto,对外提供一个OpenFlowSwitch的抽象,OpenFlow规则存储在ofproto层,OpenFlow交换机的端口抽象也在这一层,这个可以同时也可以被理解为慢速路径;2)datapath,用于连接底层的所有口,并实际进行转发,这个同时可以被理解为快速路径;3)revalidator,用于维护上下层转发语义的一致性。 以一个常见的VXLAN网关的配置来看:brex管理eth0和brex口,是underlay的网关brint管理vxlan0和vhostuser口,是overlay的网关 从图中可以看出,在slowpath,不仅仅OpenFlow会表示转发语义,配置也会表示,比如brex是一个网桥,那么就自带了二层域的概念,因此隐含了MACLearning的概念。 而在fastpath,一切都是MatchAction,端口上的其他所有属性都被剥夺了。下面举个例子,看看OVS是如何实现二层域的转发的。例子:OVS怎么实现网桥? 当你在OVS里配置一个网桥的时候,你实际上不需要配置任何OpenFlow规则。OVS默认给每个网桥配置一个动作为Normal的规则,意思就是按照Bridging的方式处理数据包。 那么OVS怎么实现网桥?很简单,套公式一样的设计:慢速路径:MacLearning 这里无需多言。当fastpathMISS之后,数据包会上送到slowpath,slowpath执行MACLearning的操作,然后下发Flooding的action。当这个MAC后续在其他口上被学习到时,Revalidator会把fastpath里Flooding的动作改成单播转发。快速路径的FEC是MAC地址 Bridge的转发依据是每个数据包的二层地址。Revalidtor 周期性的收集FEC的命中的统计信息,用于更新slowpath的MAC表,让MAC表的表项不要超时。一切都是MatchAction,配合Revalidation 不知道读者看到这里,是否会觉得OVS的设计其实挺自然的。OVS的设计,无非就是超越了MAC,IP,三层,二层,所有的协议,都基于这个套路去设计。结语 本期仅介绍了MatchActionRevalidator的套路设计,下一期介绍下OVS下一些高级概念如Recirculation,Tunnel,Conntrack的一些具体实现。如果读者对OVS如何生成FEC感兴趣,可以去看看OVS的论文:TheDesignandImplementationofOpenvSwitch。 原文链接:https:zhuanlan。zhihu。comp426498593