柔性多模正則匹配引擎

導讀: 正則表達式,每個計算機從業人員都熟知的技術,你真的懂嗎?一個老掉牙的、不時尚的技術如何在"國內首款分佈式流式關聯分析引擎sabre"中翻新?你肯定感興趣!

01 背景

正則表達式是對字符串操作的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合, 組成一個"規則字符串",這個"規則字符串"用來表達對字符串的一種過濾邏輯。正則表達式是一種文本模式,該模式描述在搜索文本時要匹配的一個或多個字符串。

用來深度包檢測的正則表達式匹配算法是網絡安全監測引擎的核心技術,但當前的正則表達式匹配引擎,在同時應對"單模正則表達式"、"數據適中 ( 一百條左右 ) 多模正則表達式"和"海量級 ( 百萬條以上 ) 多模正則表達式"時,或者匹配性能較低,或者容易出現內存溢出,均沒有實現一個切實有效的解決方案。

網絡技術不斷髮展,網絡流量不斷增長,網絡惡意行爲的種類也層出不窮,網絡安全成爲重要的、不能迴避的關鍵問題。能夠同時處理不同規模正則表達式集合,且執行時間較短的深度包檢測算法是網絡安全規則引擎的核心技術。現有技術或者匹配性能不高,或者容易發生內存溢出,均不能滿足實際應用需求。

02 創新

2.1 預處理

A)頭部預處理,用於判定是否需要在頭部增加"前綴.*"。

正則匹配模式包含兩種:搜索和匹配。"搜索"表示字符串是否包含符合正則表達式的子串,"匹配"表示整個字符串是否符合正則表達式。

如果正則匹配爲"搜索"模式,並且正則第一個字符不是"^" ( 字符^用於限定開頭 ),則需要爲此正則表達式增加"前綴.*"。例如:搜索模式的正則表達式"abc",在頭部增加"前綴.*“得到的正則表達式爲”.*abc"。

B)大小寫適配,用於處理"正則匹配是否忽略大小寫"需求。

如果正則匹配忽略大小寫,則需要將"正則表達式"和"待匹配字符串"都預先轉換爲小寫"。如果正則匹配不能忽略大小寫,則正則表達式保持不變。例如:忽略大小寫的正則表達式"^aBcD",處理之後結果爲"^abcd"。

2.2 正則表達式NFA的高效構造方法

概述:

現有的正則表達式NFA ( 非確定有窮自動機,Non-deterministic finite automaton ) 構造方法,通常分兩個步驟,首先,藉助"堆棧 ( Stack ) "構造"語法樹 ( SyntaxTree ) ",然後,將語法樹轉換爲NFA。但是,堆棧的大小不受控制,可能會出現內存溢出問題,導致程序掛掉。同時,構造語法樹也需要一定的消耗CPU。

提出一種高效的正則表達式NFA構造方法,用以解決構造時間較慢,及消耗內存不可控的問題。採用遍歷正則表達式直接構造NFA的方法,略過語法樹步驟,提升構造速度,降低內存使用,加快了正則表達式編譯效率。

詳細步驟:

遍歷正則表達式,同步構造NFA,無需創建"語法樹 ( SyntaxTree ) "。

  1. 創建"當前自動機爲nowNfa"。

  2. 遍歷正則表達式,根據"當前字符nowChar"所屬的字符類型 ( 字符轉換、量詞、或、小括號 ),分別執行對應操作。

① 字符轉換

如果"當前字符nowChar"爲"轉義字符",則解析"轉義字符"、“16進制”、“8進制"得到"結果字符集resultCharSet”。

如果"當前字符nowChar"爲"非元字符",則解析爲只包含"非元字符"的"結果字符集resultCharSet"。

如果"當前字符nowChar"爲"任意字符.",則解析爲包含"所有字符"的"結果字符集resultCharSet"。

如果"當前字符nowChar"爲"區間值[]",則解析爲包含"所有區間值"的"結果字符集resultCharSet"。

默認情況,解析爲只包含"一個字符"的"結果字符集resultCharSet"。

如果"下一個字符nextChar"爲"量詞",則將當前結果轉化爲"下一個自動機nextNfa"。否則,爲"當前自動機爲nowNfa"根據"結果字符集resultCharSet"添加跳轉操作。

② 量詞

如果“當前字符nowChar”爲“*”,則解析值爲“{0,+∞}”的“量詞區間QuantifierInterval”。

如果“當前字符nowChar”爲“?”,則解析值爲“{0,1}”的“量詞區間QuantifierInterval”。

如果“當前字符nowChar”爲“+”,則解析值爲“{1,+∞}”的“量詞區間QuantifierInterval”。

如果“當前字符nowChar”爲“{”,則解析值爲“{m,n}”的“量詞區間QuantifierInterval”。

首先針對“下一個自動機nextNfa”執行量詞操作repeat,然後針對“當前自動機爲nowNfa”和“下一個自動機nextNfa”執行連接操作connact。

③ 或

如果“當前字符nowChar”爲“|”,則針對“後續的正則表達式子串”構造“單獨的自動機newNfa”。

針對“當前自動機爲nowNfa”和“單獨的自動機newNfa”執行或操作or。

④ 小括號

如果“當前字符nowChar”爲“(”,則針對 “處於小括號內正則表達式子串”構造“單獨的自動機newNfa”。

針對“當前自動機爲nowNfa”和“單獨的自動機newNfa”執行連接操作connact。

流程圖:

2.3 具有較少空跳轉特性的正則NFA引擎構造算法

概述:

傳統的Thompson正則NFA ( 非確定有窮自動機,Non-deterministic finite automaton ) 引擎構造法,但具有“大量的空跳轉”和“較多的狀態數”。“空跳轉”使得NFA轉化爲DFA ( 確定有窮自動機,Deterministic finite automaton ) 執行時間較長。同時,“空跳轉”及“較多的狀態數”使得NFA模式正則表達式匹配速度較慢。

本發明針對正則表達式的三大基本算子 ( “連接”、“或”和“閉包” ),提出一種高效的具有較少空跳轉特性的正則NFA引擎構造算法,能夠快速地構造出“較少空跳轉”和“較少狀態數”的NFA,並且,此NFA具有較快的匹配速度。

詳細步驟:

A)連接優化,用於優化“當前自動機nowNfa”和“下一個自動機nextNfa”的連接操作。

如果“當前自動機nowNfa”爲空,則用“下一個自動機nextNfa”替換“當前自動機nowNfa”。例如:a。

如果“當前自動機nowNfa”非空,並且“下一個自動機nextNfa的頭部狀態nextHeadNfaState”不存在輸入邊,則將“當前自動機nowNfa的尾部狀態nowLastNfaState”與“下一個自動機nextNfa的頭部狀態nextHeadNfaState”合併。例如:a*b。

如果“當前自動機nowNfa”非空,並且“當前自動機nowNfa的尾部狀態nowLastNfaState”不存在輸出邊,則將“下一個自動機nextNfa的頭部狀態nextHeadNfaState”與“當前自動機nowNfa的尾部狀態nowLastNfaState”合併。例如:ab*。

如果“當前自動機nowNfa”非空,並且“下一個自動機nextNfa的頭部狀態nextHeadNfaState”存在輸入邊,並且“當前自動機nowNfa的尾部狀態nowLastNfaState”存在輸出邊,則“當前自動機nowNfa的尾部狀態nowLastNfaState”添加到“下一個自動機nextNfa的頭部狀態nextHeadNfaState”的空跳轉。例如:a*b*。

B)或優化,用於優化“當前自動機nowNfa”和“下一個自動機nextNfa”的或操作。

① 頭部狀態優化

如果“當前自動機nowNfa的頭部狀態nowHeadNfaState”不存在輸入邊,並且“下一個自動機nextNfa的頭部狀態nextHeadNfaState”不存在輸入邊,則將“當前自動機nowNfa的頭部狀態nowHeadNfaState”與“下一個自動機nextNfa的頭部狀態nextHeadNfaState”合併。例如:a|b。

如果“當前自動機nowNfa的頭部狀態nowHeadNfaState”不存在輸入邊,並且“下一個自動機nextNfa的頭部狀態nextHeadNfaState”存在輸入邊,則“當前自動機nowNfa的頭部狀態nowHeadNfaState”添加到“下一個自動機nextNfa的頭部狀態nextHeadNfaState”的空跳轉。例如:a|b*。

如果“當前自動機nowNfa的頭部狀態nowHeadNfaState”存在輸入邊,並且“下一個自動機nextNfa的頭部狀態nextHeadNfaState”不存在輸入邊,則“下一個自動機nextNfa的頭部狀態nextHeadNfaState”添加到“當前自動機nowNfa的頭部狀態nowHeadNfaState”的空跳轉。例如:a*|b。

如果“當前自動機nowNfa的頭部狀態nowHeadNfaState”存在輸入邊,並且“下一個自動機nextNfa的頭部狀態nextHeadNfaState”存在輸入邊,並且“當前自動機nowNfa的頭部狀態nowHeadNfaState”能夠與“下一個自動機nextNfa的頭部狀態nextHeadNfaState”合併,則將“當前自動機nowNfa的頭部狀態nowHeadNfaState”與“下一個自動機nextNfa的頭部狀態nextHeadNfaState”合併。例如:.*a|.*b。

如果“當前自動機nowNfa的頭部狀態nowHeadNfaState”存在輸入邊,並且“下一個自動機nextNfa的頭部狀態nextHeadNfaState”存在輸入邊,並且“當前自動機nowNfa的頭部狀態nowHeadNfaState”不能與“下一個自動機nextNfa的頭部狀態nextHeadNfaState”合併,則先爲“當前自動機nowNfa” 創建“新的頭部狀態newNowHeadNfaState”,然後爲“新的頭部狀態newNowHeadNfaState”添加到“前自動機nowNfa的頭部狀態nowHeadNfaState”的空跳轉,然後爲“新的頭部狀態newNowHeadNfaState”添加到“下一個自動機nextNfa的頭部狀態nextHeadNfaState”的空跳轉。例如:a*|b*。

② 尾部狀態優化

如果“當前自動機nowNfa的尾部狀態nowLastNfaState”不存在輸出邊,並且“下一個自動機nextNfa的尾部狀態nextLastNfaState”不存在輸出邊,則將“當前自動機nowNfa的尾部狀態nowLastNfaState”與“下一個自動機nextNfa的尾部狀態nextLastNfaState”合併。例如:a|b。

如果“當前自動機nowNfa的尾部狀態nowLastNfaState”不存在輸出邊,並且“下一個自動機nextNfa的尾部狀態nextLastNfaState”存在輸出邊,則“下一個自動機nextNfa的尾部狀態nextLastNfaState”添加到“當前自動機nowNfa的尾部狀態nowLastNfaState”的空跳轉。例如:a|b*。

如果“當前自動機nowNfa的尾部狀態nowLastNfaState”存在輸出邊,並且“下一個自動機nextNfa的尾部狀態nextLastNfaState”不存在輸出邊,則“當前自動機nowNfa的尾部狀態nowLastNfaState” 添加到“下一個自動機nextNfa的尾部狀態nextLastNfaState” 的空跳轉。例如:a*|b。

如果“當前自動機nowNfa的尾部狀態nowLastNfaState”存在輸出邊,並且“下一個自動機nextNfa的尾部狀態nextLastNfaState”存在輸出邊,並且“當前自動機nowNfa的尾部狀態nowLastNfaState”能夠與“下一個自動機nextNfa的尾部狀態nextLastNfaState”合併,則將“當前自動機nowNfa的尾部狀態nowLastNfaState”與“下一個自動機nextNfa的尾部狀態nextLastNfaState”合併。例如:a.*|b.*。

如果“當前自動機nowNfa的尾部狀態nowLastNfaState”存在輸出邊,並且“下一個自動機nextNfa的尾部狀態nextLastNfaState”存在輸出邊,並且“當前自動機nowNfa的尾部狀態nowLastNfaState” 不能與“下一個自動機nextNfa的尾部狀態nextLastNfaState”合併,則先爲“當前自動機nowNfa” 創建“新的尾部狀態newNowLastNfaState”,然後爲“當前自動機nowNfa的尾部狀態nowLastNfaState”添加到“新的頭部狀態newNowHeadNfaState”的空跳轉,然後爲“下一個自動機nextNfa的尾部狀態nextLastNfaState”添加到“新的頭部狀態newNowHeadNfaState”的空跳轉。例如:a*|b*。

C)閉包優化,用於優化“當前自動機nowNfa”的閉包操作。

將“當前自動機nowNfa的頭部狀態nowHeadNfaState”與“當前自動機nowNfa的尾部狀態nowLastNfaState”合併。例如:(ab)*。

2.4 前後綴優化的正則NFA引擎構造算法

概述:

現有的正則表達式匹配引擎,先將正則表達式編譯爲NFA(非確定有窮自動機,Non-deterministic finite automaton)。然後,如果內存空間和執行時間允許,再將NFA轉換爲DFA(確定有窮自動機,Deterministic finite automaton)。最後,根據匹配模式(“子串搜索”和“全文匹配”),執行匹配任務。但是,在“子串搜索”模式下,沒有針對前後綴“.* ”做特殊處理,導致NFA轉換爲DFA執行時間較長,並且轉換得到的DFA狀態數量較大,內存空間利用率較低。

針對“子串搜索”模式的正則表達式匹配任務,提出了一種前後綴優化的正則NFA引擎構造算法,用以解決“子串搜索”模式的正則表達式編譯期耗時較長,及內存空間利用率較低的問題。充分優化前後綴“.*”,提升了NFA轉換DFA速度,極大減少了DFA狀態數量,增加了正則表達式匹配任務使用DFA的可能性。

詳細步驟:

A)前綴優化,用於判定是否需要清除“子串搜索”模式的正則表達式前綴“.*”。

① 清除前綴“.*”。

如果輸入到“頭部NFA狀態headNfaState”的空跳轉邊爲空,並且“頭部NFA狀態headNfaState”的“輸出空跳轉邊列表epsilonEdgeList”非空,則繼續遍歷“輸出空跳轉邊列表epsilonEdgeList”,判斷“輸出空跳轉NFA狀態”是否可以清除“.*”。

如果輸入到“非頭部NFA狀態unHeadNfaState”的空跳轉邊有且僅有一條,並且輸入到自身的“區間跳轉邊gotoSelfRangeEdge”有且僅有一條,並且此區間邊包含所有字符集,則首先刪除此“區間跳轉邊gotoSelfRangeEdge”,然後判斷此“非頭部NFA狀態unHeadNfaState”的“輸出空跳轉邊列表epsilonEdgeList”是否爲空。如果非空,則繼續遍歷“輸出空跳轉邊列表epsilonEdgeList”,判斷“輸出空跳轉NFA狀態”是否可以清除“.*”。

② 合併前綴空跳轉。清除前綴“.*”後,如果非頭部NFA狀態,有到頭部NFA狀態的空跳轉,則需要將此非頭部NFA狀態與頭部NFA狀態合併。

B)後綴優化,用於判定是否需要清除“子串搜索”模式的正則表達式後綴“.*”。

① 清除後綴“.*”。

如果“尾部NFA狀態lastNfaState”的“輸出非自身空跳轉邊列表epsilonEdgeList”爲空,並且“尾部NFA狀態lastNfaState”的“輸出非自身跳轉邊gotoUnSelfRangeEdge”非空,並且輸入到自身的“區間跳轉邊gotoSelfRangeEdge”有且僅有一條,並且此區間邊包含所有字符集,則首先刪除此“區間跳轉邊gotoSelfRangeEdge”,然後判斷此“尾部NFA狀態lastNfaState”的“輸入空跳轉邊列表epsilonIntoEdgeNfaStateSet”是否爲空。如果非空,則繼續遍歷“輸入空跳轉邊列表epsilonIntoEdgeNfaStateSet”,判斷“輸入空跳轉NFA狀態”是否可以清除“.*”。

如果輸入到“非尾部NFA狀態unLastNfaState”的“輸出非自身空跳轉邊列表epsilonEdgeList”爲空,並且“輸出非自身跳轉邊gotoUnSelfRangeEdge”有且僅有一條,並且輸入到自身的“區間跳轉邊gotoSelfRangeEdge”有且僅有一條,並且此區間邊包含所有字符集,則首先刪除此“區間跳轉邊gotoSelfRangeEdge”,然後判斷此“非尾部NFA狀態unLastNfaState”的“輸入空跳轉邊列表epsilonIntoEdgeNfaStateSet”是否爲空。如果非空,則繼續遍歷“輸入空跳轉邊列表epsilonIntoEdgeNfaStateSet”,判斷“輸入空跳轉NFA狀態”是否可以清除“.*”。

② 合併後綴空跳轉。清除後綴“.*”後,如果非尾部NFA狀態,有到尾部NFA狀態的空跳轉,則需要將此非尾部NFA狀態與尾部NFA狀態合併。

2.5 快速的NFA到DFA轉換算法

概述:

現有的正則表達式匹配引擎,先將正則表達式編譯爲NFA(非確定有窮自動機,Non-deterministic finite automaton)。然後,使用“子集構造法”將NFA轉換爲DFA(確定有窮自動機,Deterministic finite automaton)。最後,採用DFA執行匹配任務。但如果採用了不合理的數據結構,NFA轉換爲DFA執行時間會較長,不僅浪費了CPU資源,而且降低了正則表達式匹配引擎的整體性能。

針對“NFA轉換爲DFA”的“子集構造法”,採用了合理的數據結構,實現了一種快速的NFA到DFA轉換算法,用以解決“NFA到DFA轉換”執行時間較長,消耗較多CPU資源的問題。採用合理的數據結構,加快了NFA轉換DFA速度,節省了CPU資源,提升了正則表達式匹配引擎的整體性能。

詳細步驟:

A)求取NFA狀態子集,用於求取DFA狀態跳轉包含的“NFA狀態集合gotoNfaStateSet”,以及對應的“跳轉NFA狀態編號有序列表gotoNfaStateIdList”。

如果“NFA狀態集合gotoNfaStateSet”只有一個NFA狀態,則“跳轉NFA狀態編號有序列表gotoNfaStateIdList”爲此NFA狀態的閉包NFA狀態編號的有序列表。

如果“NFA狀態集合gotoNfaStateSet”包含多個NFA狀態,則“跳轉NFA狀態編號有序列表gotoNfaStateIdList”爲“NFA狀態集合gotoNfaStateSet”中所有NFA狀態的閉包NFA狀態編號的有序列表。首先,創建一個“臨時跳轉閉包NFA狀態編號數組tempGotoClosureNfaStateArray”,並且此數組長度爲“NFA包含的NFA狀態總量”。然後,在此數組基礎上逐步疊加“NFA狀態的閉包NFA狀態編號”。最後,遍歷“臨時跳轉閉包NFA狀態編號數組tempGotoClosureNfaStateArray”中的有效值得到“跳轉NFA狀態編號有序列表gotoNfaStateIdList”。較傳統的Map數據結構相比,此方法具有“無須計算哈希值”和“無須比較多次”的優點。

B)創建DFA狀態,用於判定當前是否已存在DFA狀態等價於“跳轉NFA狀態編號有序列表gotoNfaStateIdList”,如果不存在,則需要創建新的DFA狀態。

採用“Radix樹”檢索“跳轉NFA狀態編號有序列表gotoNfaStateIdList”。如果存在,則說明已有等價的DFA狀態。如果不存在,則說明當前沒有與之等價的DFA狀態,需要創建新的DFA狀態newDFAState,同時需要將“跳轉NFA狀態編號有序列表gotoNfaStateIdList”和“新建的DFA狀態newDFAState”添加到“Radix樹”。較傳統的Map數據結構相比,此“Radix樹”方法無須計算哈希值,直接比較NFA狀態編號值即可,並且“Radix樹”較Map內存空間利用率更高,更適應於“NFA轉DFA”過程“DFA狀態數量爆炸”的情形。

C)構造DFA狀態跳轉,用於構造各個DFA狀態之間的跳轉關係,添加跳轉的開始字符爲gotoCharStart,添加跳轉的結束字符爲gotoCharEnd,添加跳轉的DFA狀態爲gotoDfaState。創建“跳轉邊列表gotoEdgeList”,並添加元素“ (0,255,null)”。

①“跳轉邊列表gotoEdgeList”有且只有一個元素

如果開始字符gotoCharStart等於0,並且結束字符gotoCharEnd等於“字符集最大索引charSetMaxIndex”。首先,清空“跳轉邊列表gotoEdgeList”,然後,往“跳轉邊列表gotoEdgeList” 添加元素“(gotoCharStart,gotoCharEnd,gotoDfaState)”。

如果開始字符gotoCharStart等於0,並且結束字符gotoCharEnd不等於“字符集最大索引charSetMaxIndex”。首先,清空“跳轉邊列表gotoEdgeList”,然後,往“跳轉邊列表gotoEdgeList” 添加兩個元素“(gotoCharStart,gotoCharEnd,gotoDfaState)”和“(gotoCharEnd+1,charSetMaxIndex,null)”。

如果開始字符gotoCharStart不等於0,並且結束字符gotoCharEnd等於“字符集最大索引charSetMaxIndex”。首先,清空“跳轉邊列表gotoEdgeList”,然後,往“跳轉邊列表gotoEdgeList” 添加兩個元素“(0,gotoCharStart-1,null)”和“(gotoCharStart,gotoCharEnd,gotoDfaState)”。

如果開始字符gotoCharStart不等於0,並且結束字符gotoCharEnd不等於“字符集最大索引charSetMaxIndex”。首先,清空“跳轉邊列表gotoEdgeList”,然後,往“跳轉邊列表gotoEdgeList” 添加三個元素“(0,gotoCharStart-1,null)”、“(gotoCharStart,gotoCharEnd,gotoDfaState)”和“(gotoCharEnd+1,charSetMaxIndex,null)”。

② “跳轉邊列表gotoEdgeList”有多個元素

倒數第一條跳轉邊爲end1GotoEdge,倒數第二條跳轉邊爲end2GotoEdge,倒數第二條跳轉邊的結束字符爲gotoCharEnd2,倒數第二條跳轉邊的DFA狀態爲爲nowGotoDfaState。

如果開始字符gotoCharStart等於“gotoCharEnd2+1”,並且跳轉的DFA狀態gotoDfaState與nowGotoDfaState相同,則gotoCharEnd2更新爲gotoCharEnd。

如果開始字符gotoCharStart等於“gotoCharEnd2+1”,並且跳轉的DFA狀態gotoDfaState與nowGotoDfaState不相同,則“跳轉邊列表gotoEdgeList”在倒數第一個位置添加“(gotoCharStart,gotoCharEnd,gotoDfaState)”。

如果開始字符gotoCharStart不等於“gotoCharEnd2+1”,則“跳轉邊列表gotoEdgeList”需要插入兩條新邊,即在倒數第一個位置添加“(gotoCharEnd2 + 1,gotoCharStart - 1,null)”,在最後一個位置添加“(gotoCharStart,gotoCharEnd,gotoDfaState)”。

執行完上述步驟,檢測“結束字符gotoCharEnd”是否等於“字符集最大索引charSetMaxIndex”。如果等於,則需要將“倒數第一條跳轉邊end1GotoEdge”從“跳轉邊列表gotoEdgeList”中移除。如果不等於,則將“倒數第一條跳轉邊end1GotoEdge”的開始字符修改爲“gotoCharEnd + 1”。

上述方法最多僅需要與倒數兩個元素比較,比較次數較少,執行速度較快。

2.6 高性能有限自動機空間壓縮算法

概述:

正則表達式匹配引擎,需將正則表達式對應的NFA ( 非確定有窮自動機,Non-deterministic finite automaton ) 轉換爲DFA ( 確定有窮自動機,Deterministic finite automaton ),然後採用處理速度較快的DFA執行匹配任務。然而,DFA較NFA具有極高的空間複雜度,進而影響了DFA在大規模複雜正則表達式情形下的完美使用。目前有限自動機狀態轉移採用二維矩陣存儲結構,行表示有限制動機狀態,列表示跳轉字符。但是基於二維矩陣存儲結構進行的有限自動機壓縮算法,壓縮比例較低,有限自動機空間利用率較低。

詳細步驟:

A)NFA空間壓縮,用於構造壓縮比例較高的“NFA狀態轉移”存儲數據結構。本步驟保持NFA狀態數目不變,只針對NFA狀態之間的跳轉關係做優化。

傳統的NFA二維矩陣存儲結構,行表示NFA狀態,列表示跳轉字符。但是,正則表達式中的跳轉字符可以分爲兩類:第一類是單個跳轉字符,比如:ab;第二類是跳轉字符區間(包含多個連續字符),比如:[a-h]。

基於上述理論,NFA狀態跳轉邊分“單個跳轉字符”和“跳轉字符區間”兩種情況分別存儲,其中“跳轉字符區間”可以合併爲一條存儲記錄。但是,“無效跳轉字符”及“跳轉字符區間”無須記錄,“DFA狀態之間的跳轉關係”採用鏈式列表存儲。

然後,將上述的“單個跳轉字符”和“跳轉字符區間”的存儲結構合併到“有序數組列表”,單個跳轉字符可能對應多個目的NFA狀態。此“有序數組列表”不僅可以縮短“NFA到DFA”轉換時間,而且可以提高“基於NFA匹配技術”的執行效率。

B)優化NFA到DFA轉換,用於合併NFA狀態之間的跳轉關係,進而改善“子集構造法”的性能,優化NFA到DFA轉換效率。

NFA到DFA轉換過程中,有很大一部分時間用於查詢當前跳轉字符對應的“活躍NFA狀態集”是否已經存在。但是,DFA狀態的跳轉邊往往是有限的,並且存在極大的重複概率。至少大部分情況下正則表達式爲BASE64所含的64個常用字符,重複率近似爲64/256=75%。

預處理DFA狀態包含的“活躍NFA狀態集”,多個連續的跳轉字符具有相同的“跳轉活躍NFA狀態集”,只需執行一次“Radix樹檢索”,極大減少了“Radix樹檢索次數”,進而縮小了NFA到DFA的執行時間。

C)DFA空間壓縮,用於構造壓縮比例較高的“DFA狀態轉移”存儲數據結構。本步驟保持DFA狀態數目不變,只針對DFA狀態之間的跳轉關係做優化。

傳統的DFA二維矩陣存儲結構,行表示DFA狀態,列表示跳轉字符。但是,DFA的跳轉字符可以分爲兩類:第一類是單個跳轉字符跳轉到目的DFA狀態,並且這個跳轉字符與相鄰跳轉字符目的DFA狀態不同;第二類是跳轉字符區間(包含多個連續字符)具有相同的目的DFA狀態。

基於上述理論,DFA狀態跳轉邊分“單個跳轉字符”和“跳轉字符區間”兩種情況分別存儲,其中“跳轉字符區間”可以合併爲一條存儲記錄。同時,爲了加快DFA匹配速度,“無效跳轉字符”及“跳轉字符區間”也須記錄,“DFA狀態之間的跳轉關係”採用有序數組列表存儲。

流程圖:

2.7 面向正則表達式的新型混合有限自動機

概述:

由於DFA ( 確定有窮自動機,Deterministic finite automaton ) 較NFA ( 非確定有窮自動機,Non-deterministic finite automaton ) 匹配性能較好,因此現有正則表達式匹配引擎均在滿足CPU和內存限制的情況下,盡最大可能地將NFA轉換爲DFA,後採用DFA執行匹配任務。但如果在“NFA轉換爲DFA”執行過程中超出了CPU和內存限制,則需要採用“基於NFA匹配技術”執行匹配任務。同時,現有的混合自動機,在遇到導致狀態增長 ( 比如:“.*”、“.{n}” ) 的情況就結束DFA構造,雖然包含頭部DFA和尾部NFA兩部分,但其頭部DFA具有很大不確定性,並且在利用尾部NFA時候不能很好的利用頭部DFA。

詳細步驟:

A)生成新型混合有限自動機,用於在有限時間的前提下,構造內存空間利用率較高的一種新型混合有限自動機。

① 將正則表達式格式化(補充、轉義),採用三大計算性算子(連接、並、閉包)表示。

② 將格式化的正則表達式轉化爲抽象語法樹。

③ 採用Thompson算法將抽象語法樹轉化爲NFA。

④ 在滿足執行時間和內存空間的條件下,按照“分層模式”盡最大可能的構造“半成品DFA自動機UnFinished-DFA”,“半成品UnFinished-DFA”最後一級DFA狀態標記爲“未完成DFA狀態UnFinishedDFAState”,其他DFA狀態標記爲“已完成DFA狀態FinishedDFAState”。其中,“分層模式”表示按照DFA狀態層次構造,即先構造層次較低的DFA狀態。

生成新型混合有限自動機執行參照附圖2

B)基於新型混合有限自動機執行匹配任務,用於利用性能較好的“半成品DFA自動機UnFinished-DFA”執行正則表達式匹配任務。

① 採用“半成品DFA自動機UnFinished-DFA”執行匹配任務。如果匹配失敗,則返回結果false。如果處於“未完成DFA狀態UnFinishedDFAState”,則採用“尾部NFA自動機Tail-NFA”繼續執行匹配任務。

② 採用“尾部NFA自動機Tail-NFA”執行匹配任務。

如果沒有跳轉字符對應的“活躍NFA狀態集合”,則表示匹配失敗,需返回結果false。

否則,先判斷“活躍NFA狀態集合”在“半成品DFA自動機UnFinished-DFA”是否有等價的DFA狀態。如果有等價的“已完成DFA狀態FinishedDFAState”,則複用此“已完成DFA狀態FinishedDFAState”(迴歸到“半成品DFA自動機UnFinished-DFA”匹配模式)。如果沒有等價的DFA狀態,則採用“NFA匹配模式”繼續執行。

2.8 高性能超大規模正則表達式匹配算法

概述:

海量(千萬級)正則表達式匹配引擎通常採用過濾方法實現,包含“過濾器”和“驗證器”兩大核心模塊。“過濾器”採用抽取的有效指紋構建自動機實現,“驗證器”採用NFA-DFA正則表達式引擎實現。但是,現有的“有效指紋”提取算法,均是針對“連接”操作的關鍵子串,沒有考慮正則表達式的“或”操作,進而過濾能力較低,並且“有效指紋”長度不可控,容易發生內存溢出錯誤。

提出一種高性能超大規模正則表達式匹配算法,用以解決過濾性能力較低,“自動機過濾器”內存空間不可控的問題。同時考慮正則表達的“連接”操作和“或”操作,抽取更加有效“有效指紋”,減少了需進一步驗證的正則表達式條數。控制“有效指紋”長度,進而構建內存可控的“自動機過濾器”,避免發生內存溢出錯誤。通過定序比較“有效指紋子串”的方式,提供更有效的過濾器性能。

詳細步驟:

A)定長有效指紋抽取。遍歷正則表達式,根據“當前操作”所屬類型分別執行對應“有效指紋抽取”操作。並將最原始的“有效指紋”轉化爲定長“有效指紋”。

① 抽取最原始的有效指紋

如果爲“連接”操作,則將兩個子模塊的索引連接,後面索引模塊,索引值indexValue不變,但是索引偏移indexOffset需要在前面索引模塊的基礎上遞增。例如:“abcdf”。

如果爲“或”操作,則每個子串提供相同數量的有效截取串,這幾個截取串會共用同一個“位置”,並且如果某個子串不存在有效截取串,則拋棄此“或”模塊。將兩個子模塊的索引合併,索引值indexValue可以不同,但索引偏移indexOffset必須相同。也就是,一個索引偏移indexOffset會同時對應多個不同的索引值indexValue。例如:“(abcd)|(efg)”。

如果爲“閉包/量詞”,需要進一步分類處理。形如“a{n,m}”,則等價於“a{n}”,n大於等於3,則只截取一個子串,aaa;n小於3,則和後續連接字符關聯。形如“a+”,則等價於a。

如果爲“轉義字符”,則保留“16進制”、“8進制”、“無效轉義字符”,其他跳過。

如果爲其他字符,則全部拋棄。

② 轉換爲定長有效指紋

將最原始的“有效指紋”劃分爲多個定長子串。例如:有效子串爲 “abcdef”,定長爲3,有效指紋劃分爲兩個子串“abc”和“def”。

正則表達式定長有效指紋抽取執行參照附圖2

C)自動機過濾器構建,利用已劃分的“有效指紋子串”集合構建內存可控的“自動機過濾器”。

“有效指紋子串”即爲“精確字符串”,因此可以採用“多模精確字符串匹配自動機”實現過濾器。本發明實現採用AC(Aho-Corasick automaton)自動機。

所有“有效指紋子串”均爲定長字符串,由此也限定了“自動機過濾器”的最大深度,進而控制了“自動機過濾器”內存空間。

自動機過濾器實例參照附圖3

D)有效指紋定序比較。使用“自動機過濾器”得到需要進一步驗證的正則表達式集合。

使用“自動機過濾器”匹配“待匹配字符串”,得到“待驗證正則表達式filterSuccessRegex”及其“有效指紋子串索引index”。創建存儲“待驗證正則表達式匹配進度的映射filterSuccessRegexMap”,鍵爲“待驗證正則表達式filterSuccessRegex”,值爲“有效指紋子串索引index”。

如果“待驗證正則表達式匹配進度的映射filterSuccessRegexMap”沒有對應的“待驗證正則表達式filterSuccessRegex”,並且對應的“有效指紋子串索引index”爲0,則將“待驗證正則表達式filterSuccessRegex”添加到“待驗證正則表達式匹配進度的映射filterSuccessRegexMap”,並設置其值爲“0”。

如果“待驗證正則表達式匹配進度的映射filterSuccessRegexMap”沒有對應的“待驗證正則表達式filterSuccessRegex”,並且對應的“有效指紋子串索引index”不爲0,則不做任何操作。

如果“待驗證正則表達式匹配進度的映射filterSuccessRegexMap”已有對應的“待驗證正則表達式filterSuccessRegex”,並且值等於對應的“有效指紋子串索引index”減1,則將“待驗證正則表達式匹配進度的映射filterSuccessRegexMap”鍵爲“待驗證正則表達式filterSuccessRegex”單元的值設置爲“有效指紋子串索引index”。

如果“待驗證正則表達式匹配進度的映射filterSuccessRegexMap”已有對應的“待驗證正則表達式filterSuccessRegex”,並且值不等於對應的“有效指紋子串索引index”減1,則不做任何操作。

最終,只有“待驗證正則表達式filterSuccessRegex”對應的“有效指紋子串索引index”爲正則表達式“有效指紋子串”最大索引值時,纔會進入驗證階段。

流程圖:

2.9 資源控制

03 文獻

3.1 NFA的ε跳轉優化

Jing M, Yang Y, Ning L, et al. Postfix automata[J]. Theoretical Computer Science, 2014, 562©:590-605.

3.2 自動機空間壓縮

Becchi M,Crowley P.An improved algorithm to accelerate regular expression evaluation[C]//Proceedings of the 3rd ACM/IEEE Symposium on Architecture for Networking and Communications Systems,2007:145-154.

Becchi M,Crowley P.A-DFA:a time- and space-efficient DFA compression algorithm for fast regular expression evaluation[J].ACM Transactions on Architecture and Code Optimization(TACO),2013,10(1):4.

徐乾, 鄂躍鵬, 葛敬國, et al. 深度包檢測中一種高效的正則表達式壓縮算法[J]. 軟件學報, 2009, 20(8):2214-2226.

3.3 提升匹配速度

Fu Z,Liu Z,Li J.Efficient parallelization of regular expression matching for deep inspection[C]//2017 26th International Conference on Computer Communication and Networks(ICCCN),2017:1-9.

Jiang P,Agrawal G.Combining SIMD and many/multicore parallelism for finite state machines with enumerative speculation[J].ACM SIGPLAN Notices,2017,52(8):179-191.

Kim J, Park J. FPGA-Based Memory Efficient Shift-And Algorithm for Regular Expression Matching[C]// International Symposium on Applied Reconfigurable Computing. 2018.

3.4 新型自動機

Becchi M , Crowley P . A hybrid finite automaton for practical deep packet inspection[C]// Acm Conference on Emerging Network Experiment & Technology. DBLP, 2007.

張樹壯, 吳志剛, 羅浩. 一種高效的正則表達式匹配方法[J]. 高技術通訊, 2014, 24(6):551-557.

Fu Z, Zhou S, Li J. bitFA: A Novel Data Structure for Fast and Update-friendly Regular Expression Matching[C]// Sigcomm Posters & Demos. 2017.

Yang X , Qiu T , Wang B , et al. Negative Factor:Improving Regular-Expression Matching in Strings[J]. Acm Transactions on Database Systems, 2016, 40(4):1-46.

3.5 規則拆分和分組

Becchi M , Franklin M , Crowley P . A workload for evaluating deep packet inspection architectures[C]// Workload Characterization, 2008. IISWC 2008. IEEE International Symposium on. IEEE, 2008.

柳廳文,孫永,卜東波,等. 正則表達式分組的1/(1-1/k)-近似算法[J]. 軟件學報, 2012, 23(9):2261-2272.

Liu T,Liu A X,Shi J,et al.Towards fast and optimal grouping of regular expressions via DFA size estimation[J].IEEE Journal on Selected Areas in Communications,2014,32(10):1797-1809.

Fu Z,Wang K,Cai L,et al.Intelligent and efficient grouping algorithms for large-scale regular expressions[J]. Computers & Electrical Engineering,2018,67:223-234.

3.6 工程軟件

  1. 騰訊安全零距離之大眼——大型網絡流量分析系統軟件篇

https://security.tencent.com/index.php/blog/msg/40

  1. HOW WE MATCH REGULAR EXPRESSIONS

https://www.hyperscan.io/2015/10/20/match-regular-expressions/

  1. WELCOME TO HYPERSCAN!

https://www.hyperscan.io/2015/10/13/welcome-to-hyperscan/

今天的分享就到這裏,謝謝大家。

作者介紹

王彬,奇安信異常檢測研發工程師

告警監控領域從業7年,近年發表分佈式流式異常檢測相關專利20餘個,對Flink源碼、智能異常檢測算法有深入理解。

本文來自 DataFunTalk

原文鏈接

柔性多模正則匹配引擎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章