Tor原始碼檔案分析 Cpuworker

2021-06-14 12:28:27 字數 3112 閱讀 1035

我們知道,對於tor的伺服器來說,有的時候因為其訪問量巨大,不得不採取一些相應機制來保證服務的正常提供。在伺服器程式設計裡,我們經常可以用到的技術,例如執行緒池,多路復用等。tor程式,在大多數情況下,都是單程序執行的,幾乎沒有**用到多執行緒的操作。正因為如此,tor的主程序才絕對不允許出現阻塞式的操作。但是,唯獨在一處,tor為了提高自身效率,利用了執行緒池類似的機制。這個部分就是cpuworker。本文就主要介紹該模組的作用和實現機制。

簡單的說,cpuworker存在的目的,是為了利用執行緒池的機制分擔tor主程序的壓力,幫助其在接收到create請求時計算對稱金鑰。下面進行具體的過程描述:

1. 系統啟動時根據主機cpu數量,初始化cpuworker執行緒池;(linux中線程和程序基本無差別)

cpuworkers_rotate()

1.1 根據配置檔案配置選項,自適應地檢測cpu數量或固定設定cpu數量;

spawn_enough_cpuworkers()

1.2 根據cpu數量,開啟cpuworker執行緒;(最大數目為16,最小數目為1)

spawn_cpuworker()

1.3 開啟執行緒之前,建立sockpair,建立cpuworker連線,關聯cpuworker連線與sockpair[0];

1.4 開啟執行緒之時,設定執行緒執行函式cpuworker_main,關聯執行緒與sockpair[1];

1.5 開啟執行緒之後,將cpuworker連線加入系統連線池,同時從此執行緒與tor主程序之間的通訊方式為sockpair,類似socket的讀寫操作;

2. 在成功完成以上操作之後,系統達到如下效果:

2.1 當tor主程序收到某主機發來的create包時,檢查連線池內是否有空閒的cpuworker連線;

assign_onionskin_to_cpuworker()

2.2 若有空閒cpuworker連線,則將create包內容寫入該連線;

connection_write_to_buf()

2.2.1 寫入該連線則會啟用該連線,使其將資料進一步寫到sockpair[0]內,亦即傳遞給對應的執行緒,執行緒利用sockpair[1]來讀取資料;

2.2.2 執行緒的主函式是阻塞式得等待資料,一旦資料到達,則開始處理,並在處理完畢之後,將結果寫回sockpair[1],亦即傳遞歸主程序cpuworker連線;

cpuworker_main()

2.2.3 主程序處理cpuworker讀事件就是根據對應的鏈路返回對應的created包,其中包括dh金鑰交換協議第二部分金鑰和生成的對稱金鑰的摘要等;

2.3 若無空閒cpuworker連線,則將create包掛起,在適當時候再寫入空閒連線;

onion_pending_add()

上述整個過程,省略了很多細節部分,大家各自參照原函式進行進一步分析和理解。此處對幾點再進行強調:tor系統的連線多種多樣,我們前面提過ap連線等內部連線,實際上cpuworker連線也是內部連線,雖然他是用sockpair來完成的;sockpair是一種程序間通訊機制,在眾多的通訊機制中,這種機制對tor系統最為合適,所以選用了這種方式;cpuworker執行緒的主要工作內容是對稱金鑰的生成。

這裡我們再對稱金鑰的生成進行說明:

1. 在鏈路建立的過程中,tor伺服器應該首先接收到create包。create包的負載部分具有如下格式:(針對tap握手方式)

payload := pk(padding || symmetric key || first part of g^x) || sk(second part of g^x)

pk:利用伺服器的onion key進行公鑰加密;

sk:利用symmetric key進行對稱加密;

padding:填充位元組,長度一般為42b

symmetric key:用於加密第二部分內容的對稱金鑰;

g^x:dh金鑰交換協議第一部分金鑰材料。

2. 伺服器接收到create包之後,就要決定dh金鑰交換協議的第二部分金鑰材料g^y,從而計算出對稱金鑰。就是因為這個部分的金鑰操作過程稍微會耗上一部分的時間,不適合在tor主程序中進行操作,所以tor程式利用cpuworker機制,開啟執行緒專門為這種操作提供服務。在處理玩這些操作之後,tor主程序生成created包,返回給指定的鏈路。created包的結構如下:

payload := dh_key || digest

dh_key:dh金鑰交換協議第二部分金鑰材料g^y;

digest:對稱金鑰首20位元組作為摘要;

3. 其實,對稱金鑰生成之後被作為材料,截斷成五個部分:

key material := digest || f_digest || b_digest || f_crypto || b_crypto

第一部分20位元組,被用作對稱金鑰摘要做為created包負載的乙個部分;

第二部分20位元組,被用作前向資料摘要計算的金鑰;

第三部分20位元組,被用作後向資料摘要計算的金鑰;

第四部分16位元組,被用作前向資料加密的金鑰;

第五部分16位元組,被用作後向資料加密的金鑰。

前向和後向的不同,在於資料是遠離op還是靠近op。若是遠離op,則稱為前向;若是靠近op,則稱為後向。這裡的這些金鑰,是針對op與or之間而言的,不是or與or之間的金鑰。也就是說,這些金鑰是洋蔥金鑰,用於層層包裹資料,或者層層解密資料。我們可以知道的是,前向金鑰,是用於資料遠離op時,所以是用來層層解密的;後向金鑰,是用於資料靠近op時,所以是用來層層加密的。op端存有鏈路中所有or的所有這些前後向金鑰,所以它可以完成全加密和全解密。總的來說,也就是不要忘記,這裡的金鑰協商,是op建立鏈路之時,op與or之間進行的協商。

4. 另外,在cpuworker的使用過程中,規定了一些請求和應答的格式。這個部分很簡單,就不再多說,大家可以參照cpuworker_main函式開頭部分的注釋進行理解。此處只再對tag進行簡單說明。tag的作用是進行連線id和鏈路id的記錄,方便請求處理完成之後,快速找到對應的原始鏈路。快速查詢方法就是利用之前我們提到過的雜湊對映表,這裡就不再多說:

map(circ_id, or_conn)  -->  circ

因為通過上述的講解之後,cpuworker.c原始碼部分沒有任何的理解起來的難度,所以此處就不再多原始檔進行冗餘的描述,大家可以遵循上述思路進行**的瀏覽。應該來說,很快的就可以將cpuworker的工作和整個tor系統的工作機制聯絡到一起。那麼,關於cpuworker我們就解釋到這裡。

linux原始碼「 config」檔案分析

一 config檔案概述 config檔案是linux核心配置檔案,當執行 make uimage編譯生成核心時,頂層的makefile會讀取.config檔案的內容,根據這個配置檔案來編譯所定製的核心。二 config檔案關聯 1 檔案關聯圖 以config dm9000為例進行說明。在 make...

Mangos原始碼分析(一) DBC檔案分析

2008 01 24 22 57 57 標籤 mangos 一 dbc檔案結構 檔案頭 檔案頭 4位元組 wdbc 記錄數 4位元組 記錄字段數 4位元組 每條記錄位元組數 4位元組 字串表總位元組數 4位元組 記錄1 欄位1 欄位2 欄位n 記錄2 欄位1 欄位2 欄位n 字串表 字串1 字串2 ...

spring原始碼分析 spring原始碼分析

1.spring 執行原理 spring 啟動時讀取應用程式提供的 bean 配置資訊,並在 spring 容器中生成乙份相應的 bean 配置登錄檔,然後根據這張登錄檔例項化 bean,裝配好 bean 之間的依賴關係,為上 層應用提供準備就緒的執行環境。二 spring 原始碼分析 1.1spr...