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