本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源: http://yfydz.cublog.cn
6. XFRM的其他操作
6.1 HASH处理
关于HASH值的计算方法主要在net/xfrm/xfrm_hash.h中定义:
// IPV4地址HASH
static inline unsigned int __xfrm4_addr_hash(xfrm_address_t *addr)
{
// 就是地址本身
return ntohl(addr->a4);
}
static inline unsigned int __xfrm4_addr_hash(xfrm_address_t *addr)
{
// 就是地址本身
return ntohl(addr->a4);
}
// IPV6地址HASH
static inline unsigned int __xfrm6_addr_hash(xfrm_address_t *addr)
{
// 取后2个32位数异或
return ntohl(addr->a6[2] ^ addr->a6[3]);
}
static inline unsigned int __xfrm6_addr_hash(xfrm_address_t *addr)
{
// 取后2个32位数异或
return ntohl(addr->a6[2] ^ addr->a6[3]);
}
// IPV4源,目的地址HASH
static inline unsigned int __xfrm4_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
{
// 将两个地址异或
return ntohl(daddr->a4 ^ saddr->a4);
}
static inline unsigned int __xfrm4_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
{
// 将两个地址异或
return ntohl(daddr->a4 ^ saddr->a4);
}
// IPV4源,目的地址HASH
static inline unsigned int __xfrm6_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
{
// 两个V6地址都取后2个32位数异或
return ntohl(daddr->a6[2] ^ daddr->a6[3] ^
saddr->a6[2] ^ saddr->a6[3]);
}
static inline unsigned int __xfrm6_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
{
// 两个V6地址都取后2个32位数异或
return ntohl(daddr->a6[2] ^ daddr->a6[3] ^
saddr->a6[2] ^ saddr->a6[3]);
}
// 目的地址HASH
static inline unsigned int __xfrm_dst_hash(xfrm_address_t *daddr, xfrm_address_t *saddr,
u32 reqid, unsigned short family,
unsigned int hmask)
{
// 协议族和请求ID异或
unsigned int h = family ^ reqid;
switch (family) {
// HASH值再和源目的地址HASH结果进行异或
case AF_INET:
h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);
break;
case AF_INET6:
h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);
break;
}
// 将HASH结果高低16位异或存低16位,高16位不动, 然后用HASH掩码相与
return (h ^ (h >> 16)) & hmask;
}
static inline unsigned int __xfrm_dst_hash(xfrm_address_t *daddr, xfrm_address_t *saddr,
u32 reqid, unsigned short family,
unsigned int hmask)
{
// 协议族和请求ID异或
unsigned int h = family ^ reqid;
switch (family) {
// HASH值再和源目的地址HASH结果进行异或
case AF_INET:
h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);
break;
case AF_INET6:
h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);
break;
}
// 将HASH结果高低16位异或存低16位,高16位不动, 然后用HASH掩码相与
return (h ^ (h >> 16)) & hmask;
}
// 源地址HASH, 只是没有请求ID项, 其他HASH过程和上面相同
static inline unsigned __xfrm_src_hash(xfrm_address_t *daddr,
xfrm_address_t *saddr,
unsigned short family,
unsigned int hmask)
{
unsigned int h = family;
switch (family) {
case AF_INET:
h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);
break;
case AF_INET6:
h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);
break;
};
return (h ^ (h >> 16)) & hmask;
}
// 根据SPI计算HASH值
static inline unsigned int
__xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family,
unsigned int hmask)
{
// 先将SPI和协议进行异或
unsigned int h = (__force u32)spi ^ proto;
switch (family) {
// HASH值再和目的地址进行单一地址HASH值异或
case AF_INET:
h ^= __xfrm4_addr_hash(daddr);
break;
case AF_INET6:
h ^= __xfrm6_addr_hash(daddr);
break;
}
// HASH值再和本身的高22位, 高12位异或后再和掩码相与
return (h ^ (h >> 10) ^ (h >> 20)) & hmask;
}
// 索引号HASH
static inline unsigned int __idx_hash(u32 index, unsigned int hmask)
{
// 低24位和高24位异或, 高8位不动, 再和掩码相与
return (index ^ (index >> 8)) & hmask;
}
// 选择子HASH
static inline unsigned int __sel_hash(struct xfrm_selector *sel, unsigned short family, unsigned int hmask)
{
// 提前源和目的地址
xfrm_address_t *daddr = &sel->daddr;
xfrm_address_t *saddr = &sel->saddr;
unsigned int h = 0;
switch (family) {
// 用源,目的地址同时进行HASH
case AF_INET:
if (sel->prefixlen_d != 32 ||
sel->prefixlen_s != 32)
return hmask + 1;
h = __xfrm4_daddr_saddr_hash(daddr, saddr);
break;
// 用源,目的地址同时进行HASH
case AF_INET:
if (sel->prefixlen_d != 32 ||
sel->prefixlen_s != 32)
return hmask + 1;
h = __xfrm4_daddr_saddr_hash(daddr, saddr);
break;
case AF_INET6:
if (sel->prefixlen_d != 128 ||
sel->prefixlen_s != 128)
return hmask + 1;
if (sel->prefixlen_d != 128 ||
sel->prefixlen_s != 128)
return hmask + 1;
h = __xfrm6_daddr_saddr_hash(daddr, saddr);
break;
};
// 高16位与低16位异或,高16位不变
h ^= (h >> 16);
// 与掩码相与, 其实HASH值中不带协议族因素, 因为地址本身就包含了
return h & hmask;
}
break;
};
// 高16位与低16位异或,高16位不变
h ^= (h >> 16);
// 与掩码相与, 其实HASH值中不带协议族因素, 因为地址本身就包含了
return h & hmask;
}
// 地址HASH
static inline unsigned int __addr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, unsigned int hmask)
{
unsigned int h = 0;
static inline unsigned int __addr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, unsigned int hmask)
{
unsigned int h = 0;
switch (family) {
// 用源,目的地址同时进行HASH
case AF_INET:
h = __xfrm4_daddr_saddr_hash(daddr, saddr);
break;
// 用源,目的地址同时进行HASH
case AF_INET:
h = __xfrm4_daddr_saddr_hash(daddr, saddr);
break;
case AF_INET6:
h = __xfrm6_daddr_saddr_hash(daddr, saddr);
break;
};
// 高16位与低16位异或,高16位不变
h ^= (h >> 16);
// 与掩码相与
return h & hmask;
}
h = __xfrm6_daddr_saddr_hash(daddr, saddr);
break;
};
// 高16位与低16位异或,高16位不变
h ^= (h >> 16);
// 与掩码相与
return h & hmask;
}
在net/xfrm/xfrm_hash.c 文件中定义了HASH表的分配和释放函数:
struct hlist_head *xfrm_hash_alloc(unsigned int sz)
{
struct hlist_head *n;
// 根据HASH表大小选择合适的分配方法
// 大小不超过PAGE_SIZE, 用kmalloc分配
if (sz <= PAGE_SIZE)
n = kmalloc(sz, GFP_KERNEL);
// 这是在内核定义NUMA和IA64下用vmalloc分配
else if (hashdist)
n = __vmalloc(sz, GFP_KERNEL, PAGE_KERNEL);
else
// 其他类型的内核用get_free_page分配
n = (struct hlist_head *)
__get_free_pages(GFP_KERNEL, get_order(sz));
if (sz <= PAGE_SIZE)
n = kmalloc(sz, GFP_KERNEL);
// 这是在内核定义NUMA和IA64下用vmalloc分配
else if (hashdist)
n = __vmalloc(sz, GFP_KERNEL, PAGE_KERNEL);
else
// 其他类型的内核用get_free_page分配
n = (struct hlist_head *)
__get_free_pages(GFP_KERNEL, get_order(sz));
// 空间清零
if (n)
memset(n, 0, sz);
if (n)
memset(n, 0, sz);
return n;
}
}
// 释放HASH表空间
void xfrm_hash_free(struct hlist_head *n, unsigned int sz)
{
if (sz <= PAGE_SIZE)
kfree(n);
else if (hashdist)
vfree(n);
else
free_pages((unsigned long)n, get_order(sz));
}
void xfrm_hash_free(struct hlist_head *n, unsigned int sz)
{
if (sz <= PAGE_SIZE)
kfree(n);
else if (hashdist)
vfree(n);
else
free_pages((unsigned long)n, get_order(sz));
}
6.2 算法操作
IPSEC操作中用到的认证, 加密, 压缩等算法具体实现是在crypto目录下, 而在xfrm中只是定义这些算法的说明, 表示最大可以支持这些算法, 在使用时会探测这些算法是否在内核中存在从而确定可使用的算法.
关于算法的数据结构如下:
/* include/net/xfrm.h */
// 认证算法参数
struct xfrm_algo_auth_info {
u16 icv_truncbits; // 初始向量截断位数
u16 icv_fullbits; // 初始向量总的位数
};
struct xfrm_algo_auth_info {
u16 icv_truncbits; // 初始向量截断位数
u16 icv_fullbits; // 初始向量总的位数
};
// 加密算法参数
struct xfrm_algo_encr_info {
u16 blockbits; // 块位数
u16 defkeybits; // 密钥长度位数
};
struct xfrm_algo_encr_info {
u16 blockbits; // 块位数
u16 defkeybits; // 密钥长度位数
};
// 压缩算法参数
struct xfrm_algo_comp_info {
u16 threshold; // 阈值
};
struct xfrm_algo_comp_info {
u16 threshold; // 阈值
};
// xfrm算法描述
struct xfrm_algo_desc {
char *name; // 名称
char *compat; // 名称缩写
u8 available:1; // 算法是否可用(是否在内核中)
union {
struct xfrm_algo_auth_info auth;
struct xfrm_algo_encr_info encr;
struct xfrm_algo_comp_info comp;
} uinfo; // 算法信息联合
struct sadb_alg desc; // 通用算法描述
};
struct xfrm_algo_desc {
char *name; // 名称
char *compat; // 名称缩写
u8 available:1; // 算法是否可用(是否在内核中)
union {
struct xfrm_algo_auth_info auth;
struct xfrm_algo_encr_info encr;
struct xfrm_algo_comp_info comp;
} uinfo; // 算法信息联合
struct sadb_alg desc; // 通用算法描述
};
6.2.1 认证算法
可用的认证算法通过下面的数组来描述, 包含NULL, MD5, SHA1, SHA256, RIPEMD160等认证算法:
static struct xfrm_algo_desc aalg_list[] = {
......
{
.name = "hmac(sha1)",
.compat = "sha1",
......
{
.name = "hmac(sha1)",
.compat = "sha1",
.uinfo = {
.auth = {
.icv_truncbits = 96,// 96位截断
.icv_fullbits = 160, // 总共160位
}
},
.auth = {
.icv_truncbits = 96,// 96位截断
.icv_fullbits = 160, // 总共160位
}
},
.desc = { // 这是对SHA1认证算法的标准描述参数
.sadb_alg_id = SADB_AALG_SHA1HMAC, // 算法ID值
.sadb_alg_ivlen = 0,
.sadb_alg_minbits = 160,
.sadb_alg_maxbits = 160
}
},
......
.sadb_alg_id = SADB_AALG_SHA1HMAC, // 算法ID值
.sadb_alg_ivlen = 0,
.sadb_alg_minbits = 160,
.sadb_alg_maxbits = 160
}
},
......
相关操作函数:
// 通过算法ID查找认证算法
struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id)
{
int i;
// 遍历认证数组
for (i = 0; i < aalg_entries(); i++) {
// 查找和指定算法ID相同的算法
if (aalg_list[i].desc.sadb_alg_id == alg_id) {
// 检查该算法是否可用
if (aalg_list[i].available)
return &aalg_list[i];
else
break;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(xfrm_aalg_get_byid);
struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id)
{
int i;
// 遍历认证数组
for (i = 0; i < aalg_entries(); i++) {
// 查找和指定算法ID相同的算法
if (aalg_list[i].desc.sadb_alg_id == alg_id) {
// 检查该算法是否可用
if (aalg_list[i].available)
return &aalg_list[i];
else
break;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(xfrm_aalg_get_byid);
// 统计可用的认证算法数量, 就是available的认证算法数量累加
int xfrm_count_auth_supported(void)
{
int i, n;
for (i = 0, n = 0; i < aalg_entries(); i++)
if (aalg_list[i].available)
n++;
return n;
}
EXPORT_SYMBOL_GPL(xfrm_count_auth_supported);
if (aalg_list[i].available)
n++;
return n;
}
EXPORT_SYMBOL_GPL(xfrm_count_auth_supported);
6.2.2 加密算法
可用的认证算法通过下面的数组来描述, 包含NULL, DES, 3DES, CAST, AES, BLOWFISH, TWOFISH, SERPENT等加密算法:
static struct xfrm_algo_desc ealg_list[] = {
......
{
.name = "cbc(des3_ede)",
.compat = "des3_ede",
......
{
.name = "cbc(des3_ede)",
.compat = "des3_ede",
.uinfo = {
.encr = {
.blockbits = 64,
.defkeybits = 192,
}
},
.encr = {
.blockbits = 64,
.defkeybits = 192,
}
},
.desc = {
.sadb_alg_id = SADB_EALG_3DESCBC,
.sadb_alg_ivlen = 8,
.sadb_alg_minbits = 192,
.sadb_alg_maxbits = 192
}
},
......
.sadb_alg_id = SADB_EALG_3DESCBC,
.sadb_alg_ivlen = 8,
.sadb_alg_minbits = 192,
.sadb_alg_maxbits = 192
}
},
......
// 通过算法ID查找加密算法, 和认证算法查找类似
struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id)
{
int i;
for (i = 0; i < ealg_entries(); i++) {
if (ealg_list[i].desc.sadb_alg_id == alg_id) {
if (ealg_list[i].available)
return &ealg_list[i];
else
break;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid);
if (ealg_list[i].desc.sadb_alg_id == alg_id) {
if (ealg_list[i].available)
return &ealg_list[i];
else
break;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid);
// 统计可用的加密算法数量, 就是available的加密算法数量累加
int xfrm_count_enc_supported(void)
{
int i, n;
for (i = 0, n = 0; i < ealg_entries(); i++)
if (ealg_list[i].available)
n++;
return n;
}
EXPORT_SYMBOL_GPL(xfrm_count_enc_supported);
if (ealg_list[i].available)
n++;
return n;
}
EXPORT_SYMBOL_GPL(xfrm_count_enc_supported);
6.2.3 压缩算法
可用的压缩算法通过下面的数组来描述, 包含DELFATE, LZS, LZJH等压缩算法:
static struct xfrm_algo_desc calg_list[] = {
......
{
.name = "lzs",
.uinfo = {
.comp = {
.threshold = 90,
}
},
.desc = { .sadb_alg_id = SADB_X_CALG_LZS }
},
......
......
{
.name = "lzs",
.uinfo = {
.comp = {
.threshold = 90,
}
},
.desc = { .sadb_alg_id = SADB_X_CALG_LZS }
},
......
// 通过算法ID查找加密算法, 和认证算法查找类似
struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id)
{
int i;
for (i = 0; i < calg_entries(); i++) {
if (calg_list[i].desc.sadb_alg_id == alg_id) {
if (calg_list[i].available)
return &calg_list[i];
else
break;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(xfrm_calg_get_byid);
if (calg_list[i].desc.sadb_alg_id == alg_id) {
if (calg_list[i].available)
return &calg_list[i];
else
break;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(xfrm_calg_get_byid);
6.2.4 通过名称查找算法
// 输入参数为算法数组, 数组元素个数, 类型, 掩码, 名称和是否探测在内核中存在
static struct xfrm_algo_desc *xfrm_get_byname(struct xfrm_algo_desc *list,
int entries, u32 type, u32 mask,
char *name, int probe)
{
int i, status;
if (!name)
return NULL;
// 遍历数组
for (i = 0; i < entries; i++) {
// 比较算法名称或缩写名称是否和指定名称相同
if (strcmp(name, list[i].name) &&
(!list[i].compat || strcmp(name, list[i].compat)))
continue;
// 找到算法结构
// 检查算法是否在内核可用, 可用的话成功返回
if (list[i].available)
return &list[i];
// 如果不需要探测, 将返回空
if (!probe)
break;
// 需要探测算法算法存在内核, 调用crypto_has_alg()函数探测
// 返回0表示失败, 非0表示成功
status = crypto_has_alg(name, type, mask | CRYPTO_ALG_ASYNC);
if (!status)
break;
// 算法可用, 返回
list[i].available = status;
return &list[i];
}
return NULL;
}
return NULL;
// 遍历数组
for (i = 0; i < entries; i++) {
// 比较算法名称或缩写名称是否和指定名称相同
if (strcmp(name, list[i].name) &&
(!list[i].compat || strcmp(name, list[i].compat)))
continue;
// 找到算法结构
// 检查算法是否在内核可用, 可用的话成功返回
if (list[i].available)
return &list[i];
// 如果不需要探测, 将返回空
if (!probe)
break;
// 需要探测算法算法存在内核, 调用crypto_has_alg()函数探测
// 返回0表示失败, 非0表示成功
status = crypto_has_alg(name, type, mask | CRYPTO_ALG_ASYNC);
if (!status)
break;
// 算法可用, 返回
list[i].available = status;
return &list[i];
}
return NULL;
}
/* crypto/api.c */
// 算法探测
int crypto_has_alg(const char *name, u32 type, u32 mask)
{
int ret = 0;
// 根据名称, 类型和掩码探测算法模块
struct crypto_alg *alg = crypto_alg_mod_lookup(name, type, mask);
// 正确返回找到
if (!IS_ERR(alg)) {
// 减少模块计数, 返回1
crypto_mod_put(alg);
ret = 1;
}
return ret;
}
if (!IS_ERR(alg)) {
// 减少模块计数, 返回1
crypto_mod_put(alg);
ret = 1;
}
return ret;
}
有了xfrm_get_byname()这个通用基本函数, 具体类型的算法查找函数就很简单了:
// 通过名称查找认证算法
struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name, int probe)
{
return xfrm_get_byname(aalg_list, aalg_entries(),
CRYPTO_ALG_TYPE_HASH, CRYPTO_ALG_TYPE_HASH_MASK,
name, probe);
}
EXPORT_SYMBOL_GPL(xfrm_aalg_get_byname);
// 通过名称查找加密算法
struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name, int probe)
{
return xfrm_get_byname(ealg_list, ealg_entries(),
CRYPTO_ALG_TYPE_BLKCIPHER, CRYPTO_ALG_TYPE_MASK,
name, probe);
}
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byname);
struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name, int probe)
{
return xfrm_get_byname(ealg_list, ealg_entries(),
CRYPTO_ALG_TYPE_BLKCIPHER, CRYPTO_ALG_TYPE_MASK,
name, probe);
}
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byname);
// 通过名称查找压缩算法
struct xfrm_algo_desc *xfrm_calg_get_byname(char *name, int probe)
{
return xfrm_get_byname(calg_list, calg_entries(),
CRYPTO_ALG_TYPE_COMPRESS, CRYPTO_ALG_TYPE_MASK,
name, probe);
}
EXPORT_SYMBOL_GPL(xfrm_calg_get_byname);
struct xfrm_algo_desc *xfrm_calg_get_byname(char *name, int probe)
{
return xfrm_get_byname(calg_list, calg_entries(),
CRYPTO_ALG_TYPE_COMPRESS, CRYPTO_ALG_TYPE_MASK,
name, probe);
}
EXPORT_SYMBOL_GPL(xfrm_calg_get_byname);
以下是通过索引号来查找算法, 就是直接返回相应数组指定位置的算法:
struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx)
{
if (idx >= aalg_entries())
return NULL;
return &aalg_list[idx];
}
EXPORT_SYMBOL_GPL(xfrm_aalg_get_byidx);
}
EXPORT_SYMBOL_GPL(xfrm_aalg_get_byidx);
struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx)
{
if (idx >= ealg_entries())
return NULL;
{
if (idx >= ealg_entries())
return NULL;
return &ealg_list[idx];
}
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byidx);
}
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byidx);
6.2.5 xfrm算法探测
该函数在SA进行调整时会调用来查看当前内核中支持的各种算法
/*
* Probe for the availability of crypto algorithms, and set the available
* flag for any algorithms found on the system. This is typically called by
* pfkey during userspace SA add, update or register.
*/
void xfrm_probe_algs(void)
{
// 内核必须定义CRYPTO选项, 否则就是空函数了
#ifdef CONFIG_CRYPTO
* Probe for the availability of crypto algorithms, and set the available
* flag for any algorithms found on the system. This is typically called by
* pfkey during userspace SA add, update or register.
*/
void xfrm_probe_algs(void)
{
// 内核必须定义CRYPTO选项, 否则就是空函数了
#ifdef CONFIG_CRYPTO
int i, status;
BUG_ON(in_softirq());
// 遍历认证算法数组
for (i = 0; i < aalg_entries(); i++) {
// 根据算法名称确定该HASH算法是否存在, 返回0不存在, 非0存在
status = crypto_has_hash(aalg_list[i].name, 0,
CRYPTO_ALG_ASYNC);
// 如果状态和原来的状态不同, 更改
if (aalg_list[i].available != status)
aalg_list[i].available = status;
}
// 遍历加密算法数组
for (i = 0; i < ealg_entries(); i++) {
// 根据算法名称确定该加密算法是否存在, 返回0不存在, 非0存在
status = crypto_has_blkcipher(ealg_list[i].name, 0,
CRYPTO_ALG_ASYNC);
// 如果状态和原来的状态不同, 更改
if (ealg_list[i].available != status)
ealg_list[i].available = status;
}
// 遍历压缩算法数组
for (i = 0; i < calg_entries(); i++) {
// 根据算法名称确定该压缩算法是否存在, 返回0不存在, 非0存在
status = crypto_has_comp(calg_list[i].name, 0,
CRYPTO_ALG_ASYNC);
// 如果状态和原来的状态不同, 更改
if (calg_list[i].available != status)
calg_list[i].available = status;
}
#endif
}
EXPORT_SYMBOL_GPL(xfrm_probe_algs);
BUG_ON(in_softirq());
// 遍历认证算法数组
for (i = 0; i < aalg_entries(); i++) {
// 根据算法名称确定该HASH算法是否存在, 返回0不存在, 非0存在
status = crypto_has_hash(aalg_list[i].name, 0,
CRYPTO_ALG_ASYNC);
// 如果状态和原来的状态不同, 更改
if (aalg_list[i].available != status)
aalg_list[i].available = status;
}
// 遍历加密算法数组
for (i = 0; i < ealg_entries(); i++) {
// 根据算法名称确定该加密算法是否存在, 返回0不存在, 非0存在
status = crypto_has_blkcipher(ealg_list[i].name, 0,
CRYPTO_ALG_ASYNC);
// 如果状态和原来的状态不同, 更改
if (ealg_list[i].available != status)
ealg_list[i].available = status;
}
// 遍历压缩算法数组
for (i = 0; i < calg_entries(); i++) {
// 根据算法名称确定该压缩算法是否存在, 返回0不存在, 非0存在
status = crypto_has_comp(calg_list[i].name, 0,
CRYPTO_ALG_ASYNC);
// 如果状态和原来的状态不同, 更改
if (calg_list[i].available != status)
calg_list[i].available = status;
}
#endif
}
EXPORT_SYMBOL_GPL(xfrm_probe_algs);
6.3 通过netlink套接口访问xfrm
通过netlink套接口访问xfrm的处理函数在net/xfrm/xfrm_user.c中, 提供了Linux特色的非标准PF_KEY接口的SA, SP控制方法, 能完成和PF_KEY一样控制功能, 目前iproute2中的ip工具中新增加的xfrm命令就是通过这种netlink接口来完成的, 因为netlink操作以前已经介绍过, xfrm的操作又都是一样的, 因此本文不再分析其实现过程.
6.4 xfrm_input
在net/xfrm/xfrm_input.c文件中定义了关于安全路径(struct sec_path)的几个处理函数, 用于对输入的IPSEC包进行解析构造安全路径使用.
// 释放安全路径
void __secpath_destroy(struct sec_path *sp)
{
int i;
// 减少安全路径中所有SA的使用计数
for (i = 0; i < sp->len; i++)
xfrm_state_put(sp->xvec[i]);
// 释放安全路径空间
kmem_cache_free(secpath_cachep, sp);
}
EXPORT_SYMBOL(__secpath_destroy);
void __secpath_destroy(struct sec_path *sp)
{
int i;
// 减少安全路径中所有SA的使用计数
for (i = 0; i < sp->len; i++)
xfrm_state_put(sp->xvec[i]);
// 释放安全路径空间
kmem_cache_free(secpath_cachep, sp);
}
EXPORT_SYMBOL(__secpath_destroy);
// 安全路径复制
struct sec_path *secpath_dup(struct sec_path *src)
{
struct sec_path *sp;
// 先分配安全路径结构
sp = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC);
if (!sp)
return NULL;
struct sec_path *secpath_dup(struct sec_path *src)
{
struct sec_path *sp;
// 先分配安全路径结构
sp = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC);
if (!sp)
return NULL;
sp->len = 0;
if (src) {
int i;
// 如果源安全路径结构非空, 将其全部复制到新结构中
memcpy(sp, src, sizeof(*sp));
// 增加安全路径中所有SA的使用计数
for (i = 0; i < sp->len; i++)
xfrm_state_hold(sp->xvec[i]);
}
// 设置该引用计数初始值位1
atomic_set(&sp->refcnt, 1);
return sp;
}
EXPORT_SYMBOL(secpath_dup);
if (src) {
int i;
// 如果源安全路径结构非空, 将其全部复制到新结构中
memcpy(sp, src, sizeof(*sp));
// 增加安全路径中所有SA的使用计数
for (i = 0; i < sp->len; i++)
xfrm_state_hold(sp->xvec[i]);
}
// 设置该引用计数初始值位1
atomic_set(&sp->refcnt, 1);
return sp;
}
EXPORT_SYMBOL(secpath_dup);
/* Fetch spi and seq from ipsec header */
// 从数据包中解析SPI和序号, 返回值是网络序的
int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
{
int offset, offset_seq;
// 通过nexthdr参数来判断协议类型, nexthdr是IPV6里的说法, 在IPV4中就是IP头里的协议字段
// 根据不同协议确定数据中SPI和序列号相对数据起始点的偏移
switch (nexthdr) {
case IPPROTO_AH:
offset = offsetof(struct ip_auth_hdr, spi);
offset_seq = offsetof(struct ip_auth_hdr, seq_no);
break;
case IPPROTO_ESP:
offset = offsetof(struct ip_esp_hdr, spi);
offset_seq = offsetof(struct ip_esp_hdr, seq_no);
break;
case IPPROTO_COMP:
// 对应压缩协议单独处理
// 数据头准备出IP压缩头结构长度
if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
return -EINVAL;
// SPI值取第3,4字节的数据, 序号为0
*spi = htonl(ntohs(*(__be16*)(skb->h.raw + 2)));
*seq = 0;
return 0;
default:
return 1;
}
// 数据头准备16字节空间, 这是ip_auth_hdr和ip_esp_hdr结构最小长度
if (!pskb_may_pull(skb, 16))
return -EINVAL;
// 根据偏移获取SPI和序号, 注意是网络序的值
*spi = *(__be32*)(skb->h.raw + offset);
*seq = *(__be32*)(skb->h.raw + offset_seq);
return 0;
}
EXPORT_SYMBOL(xfrm_parse_spi);
// 从数据包中解析SPI和序号, 返回值是网络序的
int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
{
int offset, offset_seq;
// 通过nexthdr参数来判断协议类型, nexthdr是IPV6里的说法, 在IPV4中就是IP头里的协议字段
// 根据不同协议确定数据中SPI和序列号相对数据起始点的偏移
switch (nexthdr) {
case IPPROTO_AH:
offset = offsetof(struct ip_auth_hdr, spi);
offset_seq = offsetof(struct ip_auth_hdr, seq_no);
break;
case IPPROTO_ESP:
offset = offsetof(struct ip_esp_hdr, spi);
offset_seq = offsetof(struct ip_esp_hdr, seq_no);
break;
case IPPROTO_COMP:
// 对应压缩协议单独处理
// 数据头准备出IP压缩头结构长度
if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
return -EINVAL;
// SPI值取第3,4字节的数据, 序号为0
*spi = htonl(ntohs(*(__be16*)(skb->h.raw + 2)));
*seq = 0;
return 0;
default:
return 1;
}
// 数据头准备16字节空间, 这是ip_auth_hdr和ip_esp_hdr结构最小长度
if (!pskb_may_pull(skb, 16))
return -EINVAL;
// 根据偏移获取SPI和序号, 注意是网络序的值
*spi = *(__be32*)(skb->h.raw + offset);
*seq = *(__be32*)(skb->h.raw + offset_seq);
return 0;
}
EXPORT_SYMBOL(xfrm_parse_spi);
...... 待续 ......