云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

域名注册_主域名解析_价格

小七 141 0

在生锈中建立快速翻译

在上一篇文章中,我们描述了防火墙规则体系结构以及不同组件是如何集成在一起的。我们还提到,我们创建了一个可配置的锈库,用于编写和执行WoRESHARK®类过滤器,它在堆栈的不同部分中写入GO、Lua、C、C++和java脚本。工人。和对性能、内存安全、低内存使用率的一系列要求,作为我们正在研究的其他产品的一部分的能力,比如光谱,锈迹是最强大的选择。我们现在已经在我们的Github帐户下公开了这个库:https://github.com/cloudflare/wirefilter。这篇文章将深入研究它的设计,解释为什么我们没有使用解析器生成器,以及我们的执行引擎如何平衡生成的解析器的安全性、运行时性能和编译成本过滤器。解析Wireshark语法在构建一个定制的领域特定语言(DSL)时,我们首先要做的就是解析它。这将产生一个中间表示(通常称为抽象语法树),可以对其进行检查、遍历、分析,并且可能,连载。在那里有不同的方法来执行这种转换,例如:使用状态机、正则表达式和/或本机字符串手动逐个字符进行解析API.解析器组合学家,它使用高级函数将不同的解析器组合在一起(在Rust land中,这些解析器由nom、chomp、combine等表示)。全自动生成器,提供语法,可以为您生成一个完全工作的解析器(例如peg、pest、LALRPOP、,Wireshark语法,但是在试图找出哪种方法最适合我们之前,让我们先看看一些简单的官方Wireshark示例,了解我们在做什么李:和蓝1500udp包含81:60:03啜饮到包含"a1762"http.request.uri匹配"gl=se$"夏令时==ff:ff:ff:ff:ff:ff:ff:地址==192.168.0.1ipv6.addr==::1可以看到比较的右侧可以是数字、IPv4/IPv6地址、一组字节或字符串。它们可以互换使用,没有任何特殊的类型概念,这是很好的,因为它们很容易区分……或者它们是吗?让我们来看看IPv6表单维基百科:2001:0db8:0000:0000:0000:ff00:0042:83292001:db8:0:0:0:ff00:42:83292001:db8::ff00:42:83292001:db8::ff00:42:8329因此IPv6可以写为一组最多8个冒号分隔的十六进制数,每个数字最多包含4个数字,前导零省略。这似乎与字节序列的语法类似。实际上,如果我们试着写出一个2f:31:32:33:34:35:36:37这样的序列,它同时也是一个有效的IPv6和Wireshark的字节序列语法。那里如果你在Wireshark中尝试使用这个序列,你会注意到它只是即:ipv6.addr==2f:31:32:33:34:35:36:37:右侧被解析并用作IPv6地址http.request.uri==2f:31:32:33:34:35:36:37:右侧被解析并用作字节序列(将与URL"/1234567"匹配)是否还有其他此类歧义的示例?是的-例如,我们可以尝试使用一个带两个十进制数的数字数字:tcp.port==80:匹配端口80上的任何流量(HTTP)http.file_数据==80:将任何HTTP请求/响应与包含单个字节(0x80)的正文相匹配我们也可以对以太网地址执行相同的操作,在Wireshark中,以太网地址被定义为单独的类型,但是为了简单起见,在我们的实现中,我们将它表示为一个常规字节序列,因此没有歧义在这里。选择解析方法这是一个有趣的语法设计决策。这意味着我们需要提前存储字段名和类型之间的映射(我们称之为Scheme),并将其用于上下文解析。这一限制还立即排除了许多解析器(如果不是大多数的话)发电机。我们仍然可以使用一个更复杂的解析器(比如LALRPOP),它允许用您自己的自定义代码替换默认的基于regex的lexer,但是在这一点上,我们已经非常接近于为DSL提供一个完整的解析器,其复杂性超过了使用的任何好处黑盒解析器发电机。相反,我们使用手动解析方法。虽然(有充分的理由)这可能听起来像C++中的不安全语言一样可怕,但是在RIST中,所有字符串都是默认的边界检查。Rust还提供了一个丰富的字符串操作API,我们可以使用它来构建更复杂的helper,最终得到一个完整的解析器。这个实际上,这种方法与解析器组合器非常相似,因为解析器不必保持状态,只将输入的未处理部分传递给更小、范围更窄的函数。就像在解析器组合器中一样,不存在可变状态也可以方便地测试和维护语法的不同部分的每个解析器其他。比较在Rust中使用流行的解析器组合器库,区别之一是我们的解析器不是独立的函数,而是实现公共的类型特征:酒吧特征LexLexResult:大小{fn lex_with(input:&'i str,extra:E)->LexResult)->bool;}它们的任意类型映射到它们的运行时提供的模式中,但与它们的输入映射到它们的上下文值很相似打电话来的。As在Scheme中,ExecutionContext最初使用一个内部HashMap来注册这些任意的String->RhsValue映射。在execute调用期间,AST实现将递归地评估自己,并查找此映射中的每个字段引用,返回值或在缺少的插槽和类型时引发错误不匹配。这个对于最初的实现来说已经足够好了,但是使用HashMap有一个非常重要的开销,我们希望消除这个开销。我们已经使用了一个更高效的hasher-Fnv,因为我们可以控制所有的密钥,因此不必担心hash-DoS攻击,但我们还可以做得更多去。超速向上字段访问如果我们查看所涉及的数据结构,我们可以看到方案总是预先定义好的,执行引擎中的所有运行时值最终都会与之匹配,即使顺序或精确的字段集不是g担保人:那又怎样如果我们完全抛弃第二个映射而使用固定大小的值数组?数组索引应该比在映射中查找便宜得多,所以它可能很值得努力。怎么做我们能做到吗?我们已经知道了项目的数量(由于预定义的方案),所以我们可以将其用于备份存储的大小,并且为了模拟未设置值的HashMap"holes",我们可以为每个项目包装一个选项:pub struct ExecutionContextLexWith用于字段{...设域=方案.get_field_索引(名称).map_err(| err |(LexErrorKind::UnknownField(err),name))?;好(字段,输入)}}之后,在ExecutionContext中,我们分配一个固定大小的数组,并使用这些索引来解析运行时:impl{///创建与给定方案关联的执行上下文。//////此方案将用于解析任何字段名和索引。p