storngswan的配置裏用一種固定格式的字符串設置了用於協商的預約義算法。在包協商過程當中strongswan將字符串轉換爲固定的枚舉值封在數據包裏用於傳輸。html
協商成功以後,這組被協商選中的枚舉值會經過netlink接口以xfrm定義好的字符串形式,傳遞給內核,內核再將字符串轉換成pfkey定義的枚舉值,最終進行加密設置。linux
DPDK的話,也有其統一的一組枚舉值的抽象。在調用不一樣的cryptodev pmd時,會想這組值轉換爲對應的值或操做,如轉變成openssl對應的API調用。git
見下圖:github
ICV:ICV有兩種翻譯,兩種解釋:Integrity Check Value, initial chaining vector。算法
一般咱們把IV理解爲: initial chaining vector; 把ICV理解爲:Integrity Check Valuec#
在strongswan的配置裏,算法是能夠經過字符串進行配置的,這樣的字符串以下:api
esp_proposals = aes128-sha384-curve25519-esn,aes128-sha384-esn
逗號分隔的是兩個套裝。橫線分隔的是不一樣的階段,加密-認證-協商-是否esnsession
手冊裏有詳細的解釋:man ipsec.conf數據結構
The notation is encryption-integrity[-dhgroup][-esnmode].
上面提到的是字符串格式的定義。具體的字符串到數據包中的值的定義,是在RFC中約定的,見文檔:函數
https://wiki.strongswan.org/projects/strongswan/wiki/IKEv2CipherSuites
https://wiki.strongswan.org/projects/strongswan/wiki/IKEv1CipherSuites
爲了使用xfrm的統一接口,strongswan的netlink模塊作了一次轉換
kernel的xfrm模塊將netlink plugin傳遞過來的string轉換成pfkey中定義的枚舉。
xfrm部分的代碼:xfrm_algo.c
pfkey中的枚舉定義:linux/pfkeyv2.h
dpdk中cryptodev一樣也有本身的封裝定義,按類別分爲對稱,非對稱等。
算法的枚舉值定義在這個地方: rte_crypto_sym.h
int rte_cryptodev_sym_session_init(uint8_t dev_id, struct rte_cryptodev_sym_session *sess, struct rte_crypto_sym_xform *xforms, struct rte_mempool *mempool);
如,開篇那個圖,全部的算法都是經過這個API註冊進dpdk的。
在這個數據結構裏,它是一個串在一塊兒的鏈表,將加密算法和消息認證碼算法串在一塊兒做爲參數傳遞。
struct rte_crypto_sym_xform { struct rte_crypto_sym_xform *next; /**< next xform in chain */ enum rte_crypto_sym_xform_type type ; /**< xform type */ RTE_STD_C11 union { struct rte_crypto_auth_xform auth; /**< Authentication / hash xform */ struct rte_crypto_cipher_xform cipher; /**< Cipher xform */ struct rte_crypto_aead_xform aead; /**< AEAD xform */ }; };
接下來逐個討論加密,消息認證和aead的結構:
struct rte_crypto_cipher_xform { enum rte_crypto_cipher_operation op; // 加密仍是解密 enum rte_crypto_cipher_algorithm algo; // 哪種加密算法 struct { uint8_t *data; uint16_t length; } key; // 祕鑰及其長度 struct { uint16_t offset; // 對於塊分組加密這裏是初始化向量的起點(rte_crypto_op??),對於CRT分組加密,這裏是counter, CCM的時候,第一個字節保留,第二個本身用來寫入nonce uint16_t length; // CRT的時候等於block length, CCM的時候是nonce的長度,7-13之間。 } iv; };
struct rte_crypto_auth_xform { enum rte_crypto_auth_operation op; enum rte_crypto_auth_algorithm algo; struct { uint8_t *data; uint16_t length; // 必須小於或等於 block size } key; struct { uint16_t offset; // 初始化向量,須要八字節對齊 uint16_t length; } iv; uint16_t digest_length; // hash算法的截斷長度 };
struct rte_crypto_aead_xform { enum rte_crypto_aead_operation op; enum rte_crypto_aead_algorithm algo; struct { uint8_t *data; uint16_t length; } key; struct { uint16_t offset; // CCM的時候,第一個字節保留,第二個字節用來寫入nonce uint16_t length; // CCM的時候,nonce的長度,7-13之間 } iv; uint16_t digest_length; uint16_t aad_length; // 附加驗證長度?? };
前文的IV offset與一個結構體相關struct rte_crypto_op cop
這個結構體是做爲參數傳遞給下面api的
static inline int rte_crypto_op_attach_sym_session(struct rte_crypto_op *op, struct rte_cryptodev_sym_session *sess)
在不使用rte_ipsec庫的狀況下,op中的一些值須要用戶關心。使用rte_ipsec庫的時候,通常不用關心。rte_ipsec將其封裝在內部處理。
在rte_ipsec庫中,圍繞這前文提到的加密算法及其參數主要作了如下三方面的動做:
1. 經過API rte_cryptodev_sym_session_init()將算法和參數下發給底層的pmd設備。
2. 爲每個須要加解密的報文準備op(rte_crypto_op),而後attach進session裏邊。經過這個函數esp_inb_tun_cop_prepare() [librte_ipsec/sa.c]
3. 爲每個須要加解密的報文準備數據包的esp封裝。經過這個函數esp_inb_tun_pkt_prepare() [librte_ipsec/sa.c]
因此在整個rte_ipsec庫的使用過程當中。只有保證以上3者的數據和設置能夠正常配合,便能保證成功使用。
詳見:[dev][dpdk][crypto] dpdk加解密設備與IPSEC
(另外:在分析rte_ipsec的源碼過程當中,並無發現對API rte_crypto_op_attach_sym_session的調用。奇怪。。。)
基於咱們對於dpdk庫的分析,此刻已經瞭解到,一個算法的基本構成單元,是由方法自己及一組參數構成的。參數爲:key長度,iv長度,digest長度,鹽等組成的。
如今回到strongswan,它的代碼裏邊除了對rfc中各個函數的定義覺得,必定還擁有這些參數值與辦法定義直接的對應關係。
前文,咱們已經知道與內核通訊時的netlink plugin中對這部分參數在傳遞過程當中進行了定義,但因爲內核的支持有限,因此也並不完整。同時,strongswan也提供了一個用戶態的esp實現,
叫作libipsec,根據文檔中的描述,該庫對rfc中的全部算法實現了完整的支持。接下來咱們對這個庫進行簡要的分析。
進入這部分功能的入口在,函數 create_traditional() [/strongswan.git/src/libipsec/esp_context.c] 中。
1. create_traditional() 會調用函數crypto_factory.c::create_crypter()
2. 以後會進入加解密plugin的create函數
METHOD(crypto_factory_t, create_crypter, crypter_t*, private_crypto_factory_t *this, encryption_algorithm_t algo, size_t key_size) { 。。。 。。。 enumerator = this->crypters->create_enumerator(this->crypters); while (enumerator->enumerate(enumerator, &entry)) { 。。。 。。。 crypter = entry->create_crypter(algo, key_size); 。。。 。。。 } 。。。 。。。 }
紅色字體是一個函數指針,是由下文中的這個宏,註冊進去的。
METHOD(plugin_t, get_features, int, private_aes_plugin_t *this, plugin_feature_t *features[]) { static plugin_feature_t f[] = { PLUGIN_REGISTER(CRYPTER, aes_crypter_create), ... ... }; ... ... }
故,逐個分析每個算法plugin的實現,將可以獲得具體的參數數值。
一些關於算法參數的細節除了能夠查看7中的實現之外,咱們還能夠查看RFC。
全部RFC的列表能夠從第二章的兩個連接關聯出來,便能針對不一樣的算法找到它所對應的RFC文件了。
在RFC中有對各類算法的參數特別詳細的描寫。
https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml
https://www.iana.org/assignments/ipsec-registry/ipsec-registry.xhtml
https://www.iana.org/assignments/isakmp-registry/isakmp-registry.xhtml