一个正式验证的NAT.docx

上传人:b****6 文档编号:8664715 上传时间:2023-02-01 格式:DOCX 页数:13 大小:38.45KB
下载 相关 举报
一个正式验证的NAT.docx_第1页
第1页 / 共13页
一个正式验证的NAT.docx_第2页
第2页 / 共13页
一个正式验证的NAT.docx_第3页
第3页 / 共13页
一个正式验证的NAT.docx_第4页
第4页 / 共13页
一个正式验证的NAT.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

一个正式验证的NAT.docx

《一个正式验证的NAT.docx》由会员分享,可在线阅读,更多相关《一个正式验证的NAT.docx(13页珍藏版)》请在冰豆网上搜索。

一个正式验证的NAT.docx

一个正式验证的NAT

一个正式验证的NAT

        一个正式验证的NAT  摘要  我们提出了一个用C语言编写的网络地址转换器,根据RFC3022的规定,它在语义上是正确的,而且没有崩溃和内存安全。

目前在网络验证方面还有很多工作,但是大多假设网络功能模型,并且证明了网络配置特有的属性,比如可达性和环路的缺失。

我们的证明直接适用于网络功能的C代码,并且证明没有实现错误。

之前的工作认为这是不可行的,但是我们证明了另外一点:

NAT是最流行的网络功能之一,并且维持每个流状态正确更新和过期,这是验证挑战的典型来源。

我们通过使用分离逻辑的符号执行和证明检查的新组合来解决可扩展性挑战;这个组合很好地匹配了网络功能的典型结构。

然后我们证明在这种情况下正式证明的正确性不会以牺牲性能为代价。

NAT代码,证明工具链和证明可以在[58]中找到。

CCS概念  网络→中间盒/网络设备;?

软件及其工程→正式软件验证;  关键词  网络功能验证;懒惰证明;符号执行  1引言  这项工作是关于设计和实施被证明是安全和正确的软件网络功能。

软件NF在低速环境中一直很受欢迎,例如家庭网关或无线接入点。

最近,他们也出现在支持多Gbps线路速率的实验性IP路器[20]和工业中间盒[8]中。

此外,我们正在目睹虚拟网络功能的推进,这些虚拟网络功能可以在通用平台上按需部署,就像虚拟机部署在云端一样。

  在网络验证方面有很多先前的工作,但据我们所知,没有任何理说NF安全性和语义正确性。

这些工作大部分依赖于与实现不同的NF的模型,因此它不      能推理后者。

Dobrescu等人是一个例外。

[19]介绍了软件数据平面验证的概念,并证明了用Click[35]编写的NF实现的低级属性。

然而,这项工作无法证明有状态的NFs的语义正确性,因为它没有对状态进行推理。

例如,尽管Dobrescuetal。

为特定的NAT实现证明无崩溃和有界执行,于没有办法推理流表的内容,所以它们不能证明它在语义上是正确的。

  我们的贡献是一个NAT函数,用C语言编写,并使用DPDK数据包处理库[21],我们证明它实现了RFC3022[53]中指定的语义,并且没有崩溃和内存安全。

我们之所以选择这个特定的NF,是因为它可以说是最受欢迎的NF之一,但事实证明很难随时间推移:

各种Cisco设备上的NAT可能会使用精心设计的输入来崩溃[17]或挂起[15];Juniper的NAT[16],WindowsServer[40]的NAT和基于NetFilter的NAT都存在类似的问题[18]。

而且,像许多NF一样,NAT保持每个流量状态都需要正确更新和过期,这是验证挑战的典型来源。

  我们在C中实现了NAT,因为这是通常用于高性能数据包处理的语言,它可以从包含DPDK的丰富而稳定的生态系统中受益。

鉴于我们无意中写了我们的NAT从头开始-而且我们的方法通常需要重构-我们确实考虑使用一种更易于验证的语言。

然而,最终我们认为,NF开发者更容易采用我们的工具集,如果它允许他们使用熟悉的语言编写代码,并利用现有的专业知识和工具,即使他们必须遵循额外的约束和注释他们的代码。

最近的工作认为,验证一个真实的,有状态的NF的C实现是不可行的符号执行[55],但我们表明,如果符号执行与其他验证技术相结合,可以完成。

  我们的方法背后的基本原理是不同的验证技术最适合不同类型的代码。

符号执行的美妙之处在于它的易用性:

它能够自动进行代码分析,因此可以被开发人员使用,而不需要验证专业知识。

符号执行的挑战是其臭名昭着的缺乏可扩展性:

将其应用于真正的C代码通常导致路径爆炸[19,55]。

通常导致无法管理的路径爆炸的真正的NF代码的一部分是操纵状态的部分。

  因此,我们将NF代码分为两部分:

一个数据结构库,保持所有“困难”的状态,然后我们正式证明这是正确的-这需要时间和形式方法的专业知识,      但可以分摊,如果库在多个NF中重用;使用库的无状态代码,我们使用符号执行自动并快速验证。

挑战在于结合这两种验证技术的结果,为此我们开发了一种我们称之为“懒惰证明”的技术。

一个懒惰的证明分支证明构成,其中顶层证明进行假定较低层次的属性,后一种证明是后验证明。

例如,符号执行需要使用必须正确的模型;我们首先做符号执行,然后才自动验证模型的正确性。

这种方法使我们可以避免必须证明我们的模型是普遍有效的-这是很难的-但是只能证明它们对于特定的NF和我们先前用符号执行来验证的特定属性是有效的,这很容易。

  我们表明,正式验证我们的NAT的正确性并不是以性能为代价:

与在DPDK上编写的未经验证的NAT相比,我们验证的NAT提供了类似的延迟,吞吐量损失小于10%。

任何我们试验过的基于DPDK的NAT,无论是否验证,都明显优于流行的Linux内置NATNetFilter。

  的其余部分结构如下:

在提供背景之后,我们用一个简单的例子来说明我们的方法,正式说明我们对NAT的证明,描述我们的验证过程,并报告我们的实验评估。

然后我们讨论限制和未来的工作,提出相关工作,并结束。

  2背景  我们的工作属于“数据平面验证”的一般领域。

这个术语通常用来表示两种不同类型的方法:

一类工作作为一个大数据平面将网络设备的配置数据平面组合在一起网络和网络属性的原因-我们称之为“网络验证”。

正交类别是于各个设备上运行的数据平面软件的属性以及软件属性的原因导致的,我们称之为“NF验证”。

在网络验证中,目标是证明特定属性保持在特定网络中,特定NF以特定方式配置和连接。

在NF验证中,目标是证明对于所有网络和工作负载,即不论NF如何配置或连接,都有一个特定属性。

在网络验证方面有很多工作[24,25,30,32,38,39,46,52,55,59]。

相比之下,NF验证的工作就少得多[19]。

网络验证的成功取决于NF验证的成功:

网络验证依赖于组成网络的      NF的模型,无论这些模型是非正式地在RFC中捕获还是在SEFL模型中更正式地[55],NICE模型[10]等。

  然而,一个基于模型的数据包总是会到达目的地的证据被一个中间件的执行错误所取代,这个错误导致数据包被丢弃,违反了模型。

有一些方法可以测试这样一个模型是否忠实于给定的实现[55],但是在测试和验证之间存在很大的差距:

一个成功测试的模型仍然可以表现出在实现中不会发生的行为,反之亦然。

然而,NF验证可以确保部署在真实网络中的NF实现确实忠实于用于验证网络的模型。

  我们的工作属于NF验证的范畴,旨在改善两个方面的技术水平:

验证高级语义属性,比如RFC的正确实现验证有状态的NF。

Dobrescu等人[19]确实验证了一个有状态的NAT,但是只证明了低层次的属性,因此没有遇到有状态NF的一些难度较大的挑战。

我们的目标是解决这些挑战,同时不要让操作员承担写作或调整模型的重任,同时要把NF执行的性能保持在与未验证的NF相同的水平。

在中,我们报告了我们在这项工作中的第一步:

开发一个有状态的,性能良好的NF,除了没有崩溃,内存错误,泄漏之外,我们还证明可以实现从RFC3022中了解的NAT语义和其他低级属性。

  3Vigor方法  为了验证我们的NAT,我们开发了一个名为Vigor的验证工具链,其中包括一个名为libVig的验证数据结构库。

我们设想Vigor围绕三种不同的开发人员角色展开软件开发过程,其中包括:

libVig开发人员,编写形式逻辑中指定公共标准的标准开发人员以及使用经过验证的NFs实现这些标准的NF开发人员。

前两个角色需要软件验证和形式化方法方面的专业知识,但是他们的时间和精力投资可以在许多共享组件中分摊,并以不同的方式实现相同的标准。

然而,担任后者角色的开发人员在验证方面几乎不需要任何专业知识。

他们是Vigor的真正受益者,因为他们现在可以编写代码,证明它们相对容易。

在中,作者承担了这三个角色,但是我们设想最终角色可以不同的专业团队甚至不同的组织来承担。

我们用一个简单的NF来说明Vigor的使用和功能,它实现了丢弃协议[48]:

一个无限循环接收来自一个接口的数据包,丢弃发送到端口9的数据包,并通过另一个接口转发剩下的数据。

      代码。

NF开发人员还编写了两个额外的东西来编写标准代码:

她注释循环并将状态封装在Vigor可以推理的libVig数据结构中。

图1显示了我们验证的实现。

它包含一个带注释的事件循环和一个用于吸收突发的环形缓冲区,通过四次调用来访问突发。

网络交互通过三个功能发生:

接收非阻塞地读取一个入站包,并将其存储在输出参数中,返回成功或失败;can_send检查是否可以发送新的数据包;并发送发送其自变量指向的数据包。

    图1丢弃协议的精确实施  循环不变量。

我们的验证过程需要循环不变式来推断循环的影响。

目前,NF开发人员以形式逻辑和C手动写入这些不变量。

在将来的工作中,我们希望能够使用现有的技术从代码中自动提取它们[23,47],或者至少自动地帮助NF开发者制定它们。

    图2图1中循环保留的不变量  ?

packetp∈rinдr,packet_constraints==true  

  

        目标属性。

Vigor证明了这个NF永远不会崩溃,它永远不会产生一个包含目标端口9的数据包。

对于后者,证明的要点是显示代码不会将目标端口9压入环形分组,并且环形不会改变存储的分组;这两个属性意味着一个弹出的数据包永远不会有目标端口9.有三个步骤:

  第一步:

功能合同和证明。

对于libVig数据类型的每种方法,libVig开发人员编写一份合同,即该方法保证的正式规范;她还写了一个正式的证明,说明该方法的实施符合合同。

这是一个重要的任务,但可以分摊到潜在的许多使用相同数据类型的NF。

图3显示了合同和ring_pop_front函数的实现,它删除了ring前面的数据包。

合同规定,这个函数不会损坏环,会移除环前面的包,并且会遵守一些对环中所有包持有的约束,只要环是在良好的状态,并在函数被调用之前尊重这些约束。

在合同中,packet_constraints_fp是一个抽象函数,即契约说ring_pop_front将会在任何包被约束之前保持任何包的约束。

NF开发人员可以在使用libVig时提供所需的约束;在这个例子中,所提供的约束也方便地用作循环不变量。

§具有libVig合同的细节。

        图3摘自ring_pop_front及其正式合约的实施  步骤2:

彻底的符号执行。

Vigor用呼叫符号模型来代替访问状态或与网络交互的所有函数调用。

例如,ring_pop_front)的符号模型通过packet_constraints返回具有完全符号内容的分组,以使其目标端口不同于9。

尽管它很简单,但是这个模型捕获了在我们的上下文中重要的ring_pop_front的所有行为,也就是说,它永远不会产生一个包含目标端口9的数据包。

一旦所有的函数调用被符号模型的调用所取代,Vigor就象征性地执行结果代码。

尽管Vigor正在执行真正的C代码,但是于模型是无状态的并且只有很少的分支点,所以这个步骤会很快结束,并且循环注释有助于防止不必要的展开。

这种彻底的符号执行有两个结果,都假设符号模型是有效的:

首先,它证明了目标低级属性成立。

其次,它产生所有可能的函数调用序列,这些序列可能是于运行代码而产生的,以及每次调用之后对程序状态的限制。

图5显示了一个这样的呼叫序列,当环充满时产生。

有关详尽的符号执行的详细信息,请参阅第节。

    图4ring_pop_front的符号模型  第3步:

懒惰模型验证。

对于访问状态的每个函数调用,在每个可行的调用序列中,Vigor验证用于在步骤2中通过符号执行产生推测验证的符号模型是回想起来的,对于该调用是有效的。

这个有效性意味着模型的输出是实际实现可能产生的输出的超集。

例如,考虑调用ring_pop_front:

Vigor在步骤2中符号执行模型之后提取对符号程序状态的约束;在调用之后插入这些所谓路径约束的断言;并要求证明检查员验证这个断言是否与ring_pop_front的合同兼容。

证明检查器得出结论,特别是模型的输出是函数的契      约指定的输出的超集,因此也是函数的实现Vigor验证在每个包send之后,目标语义属性成立,即输出包没有目标端口9。

更多细节在§中。

  无效的模型。

无效的模型会导致步骤2或步骤3失败,但是不会导致错误的证据。

例如,图4中的模型对于我们的目的来说太抽象了:

它返回的内容可能是任何东西的包,包括有一个目标端口9。

这是验证发言中的“过度近似”模型。

如果Vigor在步骤2中使用该模型,则步骤3b失败:

因为模型可以返回具有目标端口9的分组,所以Vigor无法验证所有的输出分组没有目标端口9的调用序列。

相反,图4中的模型对于我们的目的来说太具体:

它总是返回一个带有目标端口0的分组,即它是“近似”模型。

如果Vigor在步骤2中使用这个模型,则步骤3a失败:

回想一下,对于每个调用,Vigor获得在步骤2中符号执行模型之后保持的路径约束,并且在调用之后插入一个断言对于这些路径约束。

有了这个模型,断言将是//@assert。

证明检查器不能确认这个断言总是正确的,因为ring_pop_front的契约为指定了比0更宽的范围。

    图5步骤2得到的示例函数调用序列,Vigor用路径约束断言进行了注释  本节说明了Vigor如何通过函数调用序列通过证明验证缝合符号执行。

在这个证明中还有一些其他的步骤,我们在§5中做了充分的描述。

      4证明的属性  我们使用§3中概述的方法验证了我们的NAT。

我们现在描述我们验证的具体属性。

  语义属性  我们证明了VigNAT正确地实现了传统NATRFC[53]中指定的语义。

为此,我们编写了一个NAT规范,将RFC的解释形式化,我们认为这与典型的NAT实现相一致。

规范[58]有300行分离逻辑[51],需要3个人日才能完成。

  从分组到达对抽象状态的影响来看,我们从正式描述NAT行为开始,如图6所示。

有三个静态配置参数:

流量表的容量,流量超时和外部接口的IP地址。

        图6NAT语义属性的正式规范的概念总结  F函数根据流的源和目标IP地址和端口从流表中提取数据包流ID。

随着每个数据包到达,NAT发现并移除过期流,根据接收到的数据包更新流表,然后潜在地重写数据包并转发它。

为了更新流表,NAT查找与接收到的数据包具有相同流ID的所有条目,并更新它们的时间戳。

如果没有匹配的条目,并且如果数据包到达内部接口,并且如果流表未满,则NAT在流表中添加新条目。

如果此时在流  

  

        表中存在具有数据包的流ID的条目,则NAT转发数据包,根据数据包是否抵达内部或外部接口来修改其数据头;否则,它丢弃数据包。

  我们写了一个正式的机器可读的NAT标准规范,按类似的方式组织起来。

它将expire_flows,update_flow和forward合并为一个决策树。

树包含图6所示条件的分支,如P.iface=internal,以及检查相应输出的断言,例如“packetPis丢弃“或”_ip等于P.dst_ip“。

前置条件和后置条件都写在分离逻辑中,作为抽象NAT状态和/或传入/传出数据包的谓词。

决策树覆盖了每个前提条件的两个分支,因此它提供了在任何情况下NAT的行为的完整说明。

我们正式验证了VigNAT的C实现实现了从图6导出的正式规范中的行为。

  最后,于VigNAT在libVig数据结构中保持其状态,我们也证明它正确地使用了这些数据结构,即数据结构的前提条件是满足的。

  低级属性  除了NAT语义之外,我们还证明VigNAT没有下列不需要的行为:

缓冲区溢出/下溢,无效的指针解除引用,未对齐的指针,超出范围的数组索引,访问非存取器所拥有的内存,使用经过的双倍空闲类型的转换,会溢出目标,被零除,有问题的位移和整数溢出/下溢。

证明这些性质归结为证明了VigNAT代码中引入的一组表达式都能保持。

  5设计和实施  在本节中,我们将介绍VigNAT的设计以及我们如何验证其属性以及一些与实现相关的亮点。

完整的细节和源代码可以在[58]中找到。

  虽然提出的工作的目标是专门建立一个正式验证的NAT,但是我们的更广泛的目标是找到一个实用的方法来验证任何有状态的NF,所以我们采取了比绝对必要的更为原则的方法。

我们认为,实用性包括同时实现两个设计目标:

竞争性表现和低验证工作。

后者有三个组成部分:

以可验证的方式编写代码,写出证明,并验证证明。

Vigor支持C语言,因此不会给编写NF代码带来不必要      的负担,所以我们专注于设计一种高效编写和验证现实NF的技术。

与此任务相关的众所周知的验证方法包括整个程序定理证明或像符号执行的每个路径/每个状态的技术[19,55]。

与前者一样,验证财产证明是相对较快的,但是写证明是一个缓慢的,往往是手动的工作。

对于后者,于路径爆炸[19,55],验证真正的NF中的属性可能需要很长甚至是永久的,但是很容易自动化。

为了验证一个有状态的NF,这两种方法都看起来不切实际。

  在我们的方法中,我们将证明分解成几部分,并用适合于该部分的技术来证明每个部分;在那之后,我们把证据拼在一起。

我们假设大多数NFs是许多NFs共同组成的一个部分,另一部分在每个NF中是不同的。

我们也认为,随着时间的推移,NF将会聚合使用稳定的,通用的数据类型来封装NF状态,NF之间的差异将主要它们的无状态代码如何使用这些数据类型。

对于VigNAT,我们将所有的NF状态放在驻留在库中的数据结构中,并使用人工辅助的定理证明来验证这个库的正确性。

然后我们使用符号执行来证明无状态代码的正确性。

    图7VigNAT正确性证明的结构。

Pi  Pj成立  面临的挑战是如何将两种验证技术的结果联系在一起。

我们开发了懒惰的证明,一种自动将符号执行与基于分离逻辑的证明检查器接口的方法。

我们构建了一个实现这个技术的验证器,并将子证明一起粘贴到VigNAT实现NATRFC的最终证明中[53]。

  VigNAT证明五个子证明组成,如图7所示。

顶层证明目标P1是显示      VigNAT展示正确的NAT语义。

P1的证明假定了三件事:

首先,代码必须在基本意义上正常工作,如不崩溃,没有溢出。

其次,库数据结构的实现必须按照其接口契约中指定的那样工作,例如查找刚添加的流应返回该流。

第三,NF的无状态部分必须以与它们的接口一致的方式来使用数据结构,例如,指向流表的指针不会被错误地作为指向流入口的指针传入。

假定P2?

P3?

P4,验证器产生证明检查器机械验证的P1证明。

  这三个假设当然必须被证明。

为了证明P2-VigNAT满足低级属性-Vigor象征性地执行无状态代码,并检查属性是否保留在每个执行路径上。

为了扩大规模,我们采用了抽象符号模型的数据库结构。

因此,P2的证明必须假设这些模型是正确的,数据结构实现满足它们的接口,并且无状态代码正确地使用有状态数据结构。

如果这三个假设中的任何一个都不存在,则证明将不起作用。

为了证明P3,我们使用相对直接的定理证明,库的实现满足定义它的接口的契约。

  P4和P5的证明,我们发现了懒惰证明技术的第二个可扩展性好处:

它不仅允许我们用不同的工具完成证明,但也使得有可能摆脱证明较弱的属性。

例如,我们不是证明P5是普遍真实的,然后用这个证明来进一步证明P2,而是首先证明P2假设P5,之后只证明P5对于P2证明依赖于P5的具体方式成立。

对于所有可能的用例,P5的用例特定证明比证明P5更容易。

  我们现在描述Vigor的数据结构库及其正确性证明,我们的“懒惰证明”技术及其用于验证VigNAT语义的正确性,并总结了Vigor工作流程)以及我们方法的假设。

  验证的NF数据类型库  VigNAT无状态应用程序逻辑组成,这些应用程序逻辑处理Vigor库提供的数据结构中存储的状态,如散列表和数组。

例如,在§3中,我们将传入数据包放入libVig的环形数据结构中。

一般来说,无状态的NF代码应该没有任何动态分配的状态和复杂的数据结构。

它可以保留基本的程序状态,如静态分配的标量变量以及标量结构。

      处理明确的状态在必要的非类型安全程序的验证中是困难的,这主要是于追踪内存所有权和类型信息以及解开指针别名的困难。

例如,void*指针可以引用哪个内存的问题通常是不可判定的。

功能性,类型安全的语言对于验证是有吸引力的,但是对于我们来说,支持C是最重要的,并且能够验证完全状态NF。

我们通过将NF状态封装在libVig的接口之后,并且遵循规则使用指针来完成这两个任务。

虽然这种方法与所有软件都不兼容,但我们相信这是NF的一个很好的选择。

  除了NF的数据类型外,libVig还提供了一个正式的接口规范来定义这些数据类型的行为,同时也证明了libVig实现服从规范。

为了实现无状态NF代码的符号执行,libVig还提供了其数据类型的符号模型。

我们验证libVig一次,证明继承任何使用库的NF。

  本小节详细介绍了libVig实现,描述了我们使用抽象和契约来正式指定libVig的语义,说明了我们如何证明实现满足其接口规范,并提出了符号执行引擎使用的libVig数据结构的符号模型。

  实现。

竞争性能是一个重要的设计目标,libVig是优化性能的好地方。

我们做出的关键设计决定是预先分配所有libVig的内存。

虽然这缺乏在运行时动态分配的灵活性,但它提供了对内存布局的控制,使得控制高速缓存放置成为可能,并节省运行时内存管理开销。

预分配的成本可以忽略不计,我们相信预分配完全符合真实NF的使用状态。

在与Vigor的证明检查器进行验证方面,静态分配对于动态分配没有明显的好处,但对于其他检查器可能会这样做。

  在撰写时,libVig提供了开发VigNAT所需的几个基本数据结构:

流表,实现为双键哈希映射,网络流抽象,环形缓冲区,用于跟踪和过期流的expirator抽象,用于分组同质项目的分批器,用于跟踪分配端口的端口分配器,以及经典的哈希表,数组和向量。

libVig还提供nf_time抽象来访问系统时间和DPDK框架顶部的dpdk层。

  使用抽象和契约来正式指定libVig语义。

我们根据数据类型的方法操作的抽象状态来指定libVig数据类型的语义。

这与我们在形式化NATRFC中采用的方法是一样的。

每种方法的先决条件和后置条件形成了定义每种      数据类型应该做什么的合同。

  图8显示了libVig流程表的get方法的片段。

前提条件是第3-6行,后面的条件是第7-14行。

契约是为了验证者和证明检查者的消费,但是当模棱两可的自然语言或阅读源代码失败时它们也可以作为文档。

  每个都需要先决条件说明要运行的函数的要求:

它的参数和抽象状态之间的关系,或指针的内存所有权标记。

每个确保后置条件指定方法完成后保留的内容:

参数和返回值之间的关系,某个内存位置的更新值或内存所有权令牌。

  我们对指针的使用采取了“卫生”策略:

无状态代码可以在libVig接口上传递/接收指针,但是libVig数据结构对调用者来说仍然是不透明的。

无状态代码可以复制指针,分配它们,比较它们是否相等,但不能对其进行解引用。

Vigor会自动检查无状态代码是否遵守这个规范。

  验证libVig的正确性。

一旦接口的形式化完成,我们编写证明,即用断言,循环不变式等注释代码,并为证明的中间步骤定义引理。

  证明检查器首先假定前提条件,并在制定假设条件的同时逐步通过每个代码语句。

当它遇到分支条件时,它探索两个分支。

内联注释帮助检查者了解抽象状态的转换,验证它们确实对应于具体机器状态的转换。

在内存访问上,校验检查器检查地址和内存所有权令牌的有效性。

在方法调用上,它检查被调用方法的先决条件,然后采用其后置条件,实质上用假设被调用者的后置条件来替换调用。

当到达返回点时,证明检查员检

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 节日庆典

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1