hyperscan開源了!
官網:https://01.org/zh/hyperscan
1. 新聞背景
當地時間10月19日,intel將它的高速正則表達式匹配引擎hyperscan開源了,版本4.0,基於BSD許可。這個基於自動機(Automata)的引擎經過了多年開發(2008年起),經過不斷優化與完善,效率非常之高,雖然沒有pcre等對正則語法支持全面,但非常適用於網絡設備。用戶可以在網絡設備數據面(Data Plane)使用hyperscan進行規則匹配,實現高性能DPI/lPS/IDS等應用。之前開源的dpdk,搭配hyperscan,簡直是雙劍合璧。
從此之后,DPI/IPS/IDS/NGFW開發人員可以把精力更多地放在業務上,而不需要苦於優化報文轉發(dpdk之功)和規則匹配(hyperscan之力)的性能。對於DPI來說,從此之后,也許特征/規則才是核心價值,軟件本身已經不重要了。而基於dpdk的軟件產品,憑借其不輸太多的性能、較低的成本、較低的開發門檻和靈活性,也對相同功能的硬件產品發起了挑戰。
2. 技術細節
hyperscan的實現基於自動機(automata, 如NFA/DFA)而不是回溯(back-tracking)。這么做有好有壞。好處是可以支持流模式(streaming)和多模正則表達式;壞處是,基於自動機的正則匹配無法很容易地處理某些正則構造 - backreferences and arbitrary lookaround asserts(好吧,我也不知道這倆到底啥意思)是不支持的兩個主要特性。
hyperscan所使用的一些技術包括:
Discovery of literal (fixed string) factors and decomposition of regular expressions into smaller chunks (which we call "engines") separated by these literal factors.
These engines can be of many different types:
Deterministic Finite Automata (DFA)
Bit-parallel Glushkov Non-deterministic Finite Automata (NFA) engines
Custom engines for special cases (such as large bounded repeats).
These engines can take many different roles:
"Prefix" engines that precede our literal factors
"Suffix" engines that follow our literal factors
"Infix" engines that lie between two literal factors
"Outfix" engines that aren’t connected at all with literal factors (when no satisfactory factors can be found in a regular expression)
These engines can often run lazily or not at all to reduce overhead.
We merge smaller DFA/NFA engines into larger ones, where this can be done without performance loss.
SIMD "acceleration" of automata based scanning: where we can substitute relatively simple SIMD tests of our input for complex automata execution, we do it.
We use Intel SIMD instructions to handle larger-scale NFA and literal matching tasks: having 128 or 256 (or more) bits for a bit-parallel automaton is often helpful.
... and many more short-cuts to attempt to avoid doing expensive automata calculations that we ultimately won’t need.
hyperscan設計目標:
- 高性能,包括正常應用場景和邊界條件下
- 較小的database(正則表達式編譯后形成的數據)
- 運行於流模式時,較小的流狀態數據(stream state) 。這種模式下每條流都要維護自己的流狀態。
另外,還有一些設計要求或限制:
- 運行庫必須以C實現,因為一些數據面環境不支持C++
- 不可以在運行時任意請求內存,用到的內存僅包括database、匹配臨時數據(scratch)和流狀態(在流模式下)。
- database必須是平面化(flat)的內存布局,以便可以序列化/反序列化,或者可以內存中的一處移動到另一處(這意味着內部不能含有指針)
3. API介紹
hyperscan以C++實現,使用了boost庫和C++11特性,但它的API卻以簡潔的C形式提供。hyperscan API主要分為兩個部分:編譯與匹配 (compile & scan)。用戶提供的多個正則表達式先要經過編譯,生成database,然后才可以調用匹配接口,使用此database進行匹配。
編譯就是將多個正則表達式編譯為hyperscan database,調用編譯接口時可以傳入一些flags和mode等參數,控制匹配行為和運行模式。hyperscan的運行模式主要有3個:BLOCK、STREAM和VECTORED。BLOCK模式就是對多個數據塊分別進行匹配;STREAM模式將特定的一組數據庫視為一個STREAM,為每一個STREAM維護狀態信息,它可以跨數據塊進行匹配;VECTORED模式可以一次匹配多個數據塊。使用不同模式參數編譯的database在匹配時不能混用。
hyperscan的database可以序列化成文件(當然也可以反序列化),也可以在不同機器間進行傳遞。
編譯的API主要有
hs_compile()
hs_compile_multi()
hs_compile_ext_multi()
匹配就是基於編譯好的database,對數據進行匹配,並得到匹配結果。 hyperscan在進行匹配時需要有一個臨時數據(scratch),這塊數據需要在數據面運行前就分配(不在運行時分配和釋放,保證性能),且需要保證同一時刻僅有一個匹配過程在使用同一塊臨時數據。如果使用流模式,還需要預先為每個流分配流狀態數據。
匹配結果是通過用戶自定義的回調函數獲取的。匹配過程中只要發生命中,就會調用此函數。此函數返回0表示繼續匹配,非0表示停止匹配。此函數的原型由hyperscan約定,用戶通過實參,可以獲得命中正則表達式對應的ID、匹配位置等信息。
匹配回調函數的原型:
typedef (* match_event_handler)(unsigned int id, unsigned long long from, unsigned long long to, unsigned int flags, void *context)
匹配的API主要有
hs_scan()
hs_scan_vector()
hs_scan_stream()
流模式的匹配較為復雜,這里不提及,下文有相關示例解讀。
