- 浏览: 265056 次
- 性别:
- 来自: 吉林
-
文章分类
最新评论
-
hhb19900618:
你好 请问怎么处理 客户端 socket发送数据超时后的崩溃问 ...
c socket 编程(TCP服务器端代码) -
gar_odie:
挺好的,谢谢露珠
jquery table分页插件 -
a545807638:
...
jquery table分页插件 -
a06062125:
挺好!值得学习
二叉查找树 --c代码 -
a06062125:
只有代码没有讲解 这也算日志
kmp 算法 --转
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
8. 安全协议
与IPSEC相关的安全协议是AH(51)和ESP(50), IPSEC使用这两个协议对普通数据包进行封装, AH只认证不加密, ESP既加密又认证, 当ESP和AH同时使用时, 一般都是先进行ESP封装, 再进行AH封装, 因为AH是对整个IP包进行验证的, 而ESP只验证负载部分.
在IPV4下的AH和ESP的协议实现在net/ipv4/ah4.c和net/ipv4/esp4.c中, 每个协议实现实际是要完成两个结构: struct net_protocol和struct xfrm_type, 前者用于处理接收的该协议类型的IP包, 后者则是IPSEC协议处理.
8.1 AH
8.1.1 初始化
/* net/ipv4/ah4.c */
static int __init ah4_init(void)
{
// 登记AH协议的xfrm协议处理结构
if (xfrm_register_type(&ah_type, AF_INET) < 0) {
printk(KERN_INFO "ip ah init: can't add xfrm type\n");
return -EAGAIN;
}
// 登记AH协议到IP协议
if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
printk(KERN_INFO "ip ah init: can't add protocol\n");
xfrm_unregister_type(&ah_type, AF_INET);
return -EAGAIN;
}
return 0;
}
8.1.2 IPV4下的AH协议处理结构
// AH协议处理结构, 接收到IPV4包后, 系统根据IP头中的protocol字段选择相应的上层协议处理
// 函数, 当IP协议号是51时, 数据包将调用该结构的handler处理函数:
static struct net_protocol ah4_protocol = {
.handler = xfrm4_rcv,
.err_handler = ah4_err,
.no_policy = 1,
};
AH协议结构的handler函数为xfrm4_rcv, 在net/ipv4/xfrm4_input.c 中定义, 在上一篇中进行了介绍.
// 错误处理, 收到ICMP错误包时的处理情况, 此时的skb包是ICMP包
static void ah4_err(struct sk_buff *skb, u32 info)
{
// 应用层, data指向ICMP错误包里的内部IP头
struct iphdr *iph = (struct iphdr*)skb->data;
// AH头
struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+(iph->ihl<<2));
struct xfrm_state *x;
// ICMP错误类型检查, 本处理函数只处理"目的不可达"和"需要分片"两种错误
if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
// 重新查找SA
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET);
if (!x)
return;
printk(KERN_DEBUG "pmtu discovery on SA AH/%08x/%08x\n",
ntohl(ah->spi), ntohl(iph->daddr));
xfrm_state_put(x);
}
8.1.3 AH4协议的IPSEC处理结构
// AH4的xfrm协议处理结构
static struct xfrm_type ah_type =
{
.description = "AH4",
.owner = THIS_MODULE,
.proto = IPPROTO_AH,
// 状态初始化
.init_state = ah_init_state,
// 协议释放
.destructor = ah_destroy,
// 协议输入
.input = ah_input,
// 协议输出
.output = ah_output
};
结构的重点是input和ouput函数
8.1.3.1 状态初始化
ah_data数据结构:
/* include/net/ah.h */
struct ah_data
{
// 密钥指针
u8 *key;
// 密钥长度
int key_len;
// 工作初始化向量
u8 *work_icv;
// 初始化向量完整长度
int icv_full_len;
// 初始化向量截断长度
int icv_trunc_len;
// HASH算法
struct crypto_hash *tfm;
};
{
// 密钥指针
u8 *key;
// 密钥长度
int key_len;
// 工作初始化向量
u8 *work_icv;
// 初始化向量完整长度
int icv_full_len;
// 初始化向量截断长度
int icv_trunc_len;
// HASH算法
struct crypto_hash *tfm;
};
// 该函数被xfrm状态(SA)初始化函数xfrm_init_state调用
// 用来生成SA中所用的AH数据处理结构相关信息
static int ah_init_state(struct xfrm_state *x)
{
struct ah_data *ahp = NULL;
struct xfrm_algo_desc *aalg_desc;
struct crypto_hash *tfm;
// 对AH协议的SA, 认证算法是必须的, 否则就没法进行AH认证了
if (!x->aalg)
goto error;
if (!x->aalg)
goto error;
/* null auth can use a zero length key */
// 认证算法密钥长度要大于512
if (x->aalg->alg_key_len > 512)
goto error;
// 认证算法密钥长度要大于512
if (x->aalg->alg_key_len > 512)
goto error;
// 如果要进行UDP封装(进行NAT穿越), 错误, 因为AH是不支持NAT的
if (x->encap)
goto error;
if (x->encap)
goto error;
// 分配ah_data数据结构空间
ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);
if (ahp == NULL)
return -ENOMEM;
ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);
if (ahp == NULL)
return -ENOMEM;
// 设置AH数据结构的密钥和长度
ahp->key = x->aalg->alg_key;
ahp->key_len = (x->aalg->alg_key_len+7)/8;
// 分配认证算法HASH结构指针并赋值给AH数据结构
// 算法是固定相同的, 但在每个应用使用算法时的上下文是不同的, 该结构就是描述具体应用
// 时的相关处理的上下文数据的
tfm = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
goto error;
ahp->key = x->aalg->alg_key;
ahp->key_len = (x->aalg->alg_key_len+7)/8;
// 分配认证算法HASH结构指针并赋值给AH数据结构
// 算法是固定相同的, 但在每个应用使用算法时的上下文是不同的, 该结构就是描述具体应用
// 时的相关处理的上下文数据的
tfm = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
goto error;
ahp->tfm = tfm;
// 设置认证算法密钥
if (crypto_hash_setkey(tfm, ahp->key, ahp->key_len))
goto error;
/*
* Lookup the algorithm description maintained by xfrm_algo,
* verify crypto transform properties, and store information
* we need for AH processing. This lookup cannot fail here
* after a successful crypto_alloc_hash().
*/
// 分配算法描述结构
aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
BUG_ON(!aalg_desc);
// 设置认证算法密钥
if (crypto_hash_setkey(tfm, ahp->key, ahp->key_len))
goto error;
/*
* Lookup the algorithm description maintained by xfrm_algo,
* verify crypto transform properties, and store information
* we need for AH processing. This lookup cannot fail here
* after a successful crypto_alloc_hash().
*/
// 分配算法描述结构
aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
BUG_ON(!aalg_desc);
if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
crypto_hash_digestsize(tfm)) {
printk(KERN_INFO "AH: %s digestsize %u != %hu\n",
x->aalg->alg_name, crypto_hash_digestsize(tfm),
aalg_desc->uinfo.auth.icv_fullbits/8);
goto error;
}
// AH数据结构的初始化向量的总长和截断长度的赋值
ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
ahp->icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN);
crypto_hash_digestsize(tfm)) {
printk(KERN_INFO "AH: %s digestsize %u != %hu\n",
x->aalg->alg_name, crypto_hash_digestsize(tfm),
aalg_desc->uinfo.auth.icv_fullbits/8);
goto error;
}
// AH数据结构的初始化向量的总长和截断长度的赋值
ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
ahp->icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN);
// 分配初始化向量空间, 没对其赋值, 其初始值就是随机值, 这也是初始化向量所需要的
ahp->work_icv = kmalloc(ahp->icv_full_len, GFP_KERNEL);
if (!ahp->work_icv)
goto error;
// AH类型SA中AH头长度: ip_auth_hdr结构和初始化向量长度, 按8字节对齐
// 反映在AH封装操作时要将数据包增加的长度
x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_trunc_len);
// 如果是通道模式, 增加IP头长度
if (x->props.mode == XFRM_MODE_TUNNEL)
x->props.header_len += sizeof(struct iphdr);
// SA数据指向AH数据结构
x->data = ahp;
ahp->work_icv = kmalloc(ahp->icv_full_len, GFP_KERNEL);
if (!ahp->work_icv)
goto error;
// AH类型SA中AH头长度: ip_auth_hdr结构和初始化向量长度, 按8字节对齐
// 反映在AH封装操作时要将数据包增加的长度
x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_trunc_len);
// 如果是通道模式, 增加IP头长度
if (x->props.mode == XFRM_MODE_TUNNEL)
x->props.header_len += sizeof(struct iphdr);
// SA数据指向AH数据结构
x->data = ahp;
return 0;
error:
if (ahp) {
kfree(ahp->work_icv);
crypto_free_hash(ahp->tfm);
kfree(ahp);
}
return -EINVAL;
}
if (ahp) {
kfree(ahp->work_icv);
crypto_free_hash(ahp->tfm);
kfree(ahp);
}
return -EINVAL;
}
8.1.3.2 协议释放
// 该函数被xfrm状态(SA)释放函数xfrm_state_gc_destroy()调用
static void ah_destroy(struct xfrm_state *x)
{
struct ah_data *ahp = x->data;
static void ah_destroy(struct xfrm_state *x)
{
struct ah_data *ahp = x->data;
if (!ahp)
return;
// 释放初始化向量空间
kfree(ahp->work_icv);
ahp->work_icv = NULL;
// 算法描述释放
crypto_free_hash(ahp->tfm);
ahp->tfm = NULL;
// AH数据结构释放
kfree(ahp);
}
return;
// 释放初始化向量空间
kfree(ahp->work_icv);
ahp->work_icv = NULL;
// 算法描述释放
crypto_free_hash(ahp->tfm);
ahp->tfm = NULL;
// AH数据结构释放
kfree(ahp);
}
8.1.3.3 协议输入
// 接收数据处理, 在xfrm4_rcv_encap()函数中调用
// 进行AH认证, 剥离AH头
static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
{
int ah_hlen;
int ihl;
int err = -EINVAL;
struct iphdr *iph;
struct ip_auth_hdr *ah;
struct ah_data *ahp;
// IP头备份空间
char work_buf[60];
// skb数据包要准备留出AH头空间
if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))
goto out;
// IP上层数据为AH数据
ah = (struct ip_auth_hdr*)skb->data;
// SA相关的AH处理数据
ahp = x->data;
ah_hlen = (ah->hdrlen + 2) << 2;
// AH头部长度合法性检查
if (ah_hlen != XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_full_len) &&
ah_hlen != XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_trunc_len))
goto out;
if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))
goto out;
// IP上层数据为AH数据
ah = (struct ip_auth_hdr*)skb->data;
// SA相关的AH处理数据
ahp = x->data;
ah_hlen = (ah->hdrlen + 2) << 2;
// AH头部长度合法性检查
if (ah_hlen != XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_full_len) &&
ah_hlen != XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_trunc_len))
goto out;
// skb数据包要准备留出实际AH头空间
if (!pskb_may_pull(skb, ah_hlen))
goto out;
if (!pskb_may_pull(skb, ah_hlen))
goto out;
/* We are going to _remove_ AH header to keep sockets happy,
* so... Later this can change. */
// 对于clone的包要复制成独立包
if (skb_cloned(skb) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
goto out;
* so... Later this can change. */
// 对于clone的包要复制成独立包
if (skb_cloned(skb) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
goto out;
skb->ip_summed = CHECKSUM_NONE;
// 可能包已经进行了复制, 所以对ah重新赋值
ah = (struct ip_auth_hdr*)skb->data;
iph = skb->nh.iph;
// IP头长度
ihl = skb->data - skb->nh.raw;
// 备份外部IP头数据
memcpy(work_buf, iph, ihl);
// 可能包已经进行了复制, 所以对ah重新赋值
ah = (struct ip_auth_hdr*)skb->data;
iph = skb->nh.iph;
// IP头长度
ihl = skb->data - skb->nh.raw;
// 备份外部IP头数据
memcpy(work_buf, iph, ihl);
// 将IP头中的一些参数清零, 这些参数不进行认证
iph->ttl = 0;
iph->tos = 0;
iph->frag_off = 0;
iph->check = 0;
// IP头长度超过20字节时,处理IP选项参数
if (ihl > sizeof(*iph)) {
u32 dummy;
if (ip_clear_mutable_options(iph, &dummy))
goto out;
}
{
// 认证数据缓冲区
u8 auth_data[MAX_AH_AUTH_LEN];
// 拷贝数据包中的认证数据到缓冲区
memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
// 包括IP头部分数据
skb_push(skb, ihl);
// 计算认证值是否匹配, 非0表示出错
err = ah_mac_digest(ahp, skb, ah->auth_data);
// 认证失败返回错误
if (err)
goto out;
err = -EINVAL;
// 复制一定长度的认证数据作为初始化向量
if (memcmp(ahp->work_icv, auth_data, ahp->icv_trunc_len)) {
x->stats.integrity_failed++;
goto out;
}
}
// 将备份的IP头缓冲区中的协议改为AH内部包裹的协议
((struct iphdr*)work_buf)->protocol = ah->nexthdr;
// 将原来IP头数据拷贝到原来AH头后面作为新IP头
skb->h.raw = memcpy(skb->nh.raw += ah_hlen, work_buf, ihl);
// skb包缩减原来的IP头和AH头, 以新IP头作为数据开始
__skb_pull(skb, ah_hlen + ihl);
iph->ttl = 0;
iph->tos = 0;
iph->frag_off = 0;
iph->check = 0;
// IP头长度超过20字节时,处理IP选项参数
if (ihl > sizeof(*iph)) {
u32 dummy;
if (ip_clear_mutable_options(iph, &dummy))
goto out;
}
{
// 认证数据缓冲区
u8 auth_data[MAX_AH_AUTH_LEN];
// 拷贝数据包中的认证数据到缓冲区
memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
// 包括IP头部分数据
skb_push(skb, ihl);
// 计算认证值是否匹配, 非0表示出错
err = ah_mac_digest(ahp, skb, ah->auth_data);
// 认证失败返回错误
if (err)
goto out;
err = -EINVAL;
// 复制一定长度的认证数据作为初始化向量
if (memcmp(ahp->work_icv, auth_data, ahp->icv_trunc_len)) {
x->stats.integrity_failed++;
goto out;
}
}
// 将备份的IP头缓冲区中的协议改为AH内部包裹的协议
((struct iphdr*)work_buf)->protocol = ah->nexthdr;
// 将原来IP头数据拷贝到原来AH头后面作为新IP头
skb->h.raw = memcpy(skb->nh.raw += ah_hlen, work_buf, ihl);
// skb包缩减原来的IP头和AH头, 以新IP头作为数据开始
__skb_pull(skb, ah_hlen + ihl);
return 0;
out:
return err;
}
return err;
}
8.1.3.4 协议输出
// 发送数据处理, 在xfrm4_output_one()中调用
// 计算AH认证值, 添加AH头
static int ah_output(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
struct iphdr *iph, *top_iph;
struct ip_auth_hdr *ah;
struct ah_data *ahp;
// 临时IP头缓冲区, 最大IP头60字节
union {
struct iphdr iph;
char buf[60];
} tmp_iph;
// 当前的IP头将作为最外部IP头
top_iph = skb->nh.iph;
// 临时IP头,用于临时保存IP头内部分字段数据
iph = &tmp_iph.iph;
top_iph = skb->nh.iph;
// 临时IP头,用于临时保存IP头内部分字段数据
iph = &tmp_iph.iph;
// 将当前IP头中不进行认证的字段数据复制到临时IP头
iph->tos = top_iph->tos;
iph->ttl = top_iph->ttl;
iph->frag_off = top_iph->frag_off;
// 如果有IP选项, 处理IP选项
if (top_iph->ihl != 5) {
iph->daddr = top_iph->daddr;
memcpy(iph+1, top_iph+1, top_iph->ihl*4 - sizeof(struct iphdr));
err = ip_clear_mutable_options(top_iph, &top_iph->daddr);
if (err)
goto error;
}
// AH头定位在外部IP头后面, skb缓冲中已经预留出AH头的数据部分了,
// 这是通过mode->output函数预留的, 通常调用type->output前要调用mode->oputput
ah = (struct ip_auth_hdr *)((char *)top_iph+top_iph->ihl*4);
// AH中的下一个头用原来的外部IP头中的协议
ah->nexthdr = top_iph->protocol;
// 将外部IP头的不进行认证计算的部分字段清零
top_iph->tos = 0;
top_iph->tot_len = htons(skb->len);
top_iph->frag_off = 0;
top_iph->ttl = 0;
// IP协议改为AH
top_iph->protocol = IPPROTO_AH;
top_iph->check = 0;
iph->tos = top_iph->tos;
iph->ttl = top_iph->ttl;
iph->frag_off = top_iph->frag_off;
// 如果有IP选项, 处理IP选项
if (top_iph->ihl != 5) {
iph->daddr = top_iph->daddr;
memcpy(iph+1, top_iph+1, top_iph->ihl*4 - sizeof(struct iphdr));
err = ip_clear_mutable_options(top_iph, &top_iph->daddr);
if (err)
goto error;
}
// AH头定位在外部IP头后面, skb缓冲中已经预留出AH头的数据部分了,
// 这是通过mode->output函数预留的, 通常调用type->output前要调用mode->oputput
ah = (struct ip_auth_hdr *)((char *)top_iph+top_iph->ihl*4);
// AH中的下一个头用原来的外部IP头中的协议
ah->nexthdr = top_iph->protocol;
// 将外部IP头的不进行认证计算的部分字段清零
top_iph->tos = 0;
top_iph->tot_len = htons(skb->len);
top_iph->frag_off = 0;
top_iph->ttl = 0;
// IP协议改为AH
top_iph->protocol = IPPROTO_AH;
top_iph->check = 0;
// AH数据处理结构
ahp = x->data;
// AH头长度对齐
ah->hdrlen = (XFRM_ALIGN8(sizeof(struct ip_auth_hdr) +
ahp->icv_trunc_len) >> 2) - 2;
// AH头参数赋值
ah->reserved = 0;
// SPI值
ah->spi = x->id.spi;
// 序列号
ah->seq_no = htonl(++x->replay.oseq);
// 通知防止重放攻击处理, 更新序列号
xfrm_aevent_doreplay(x);
// 对skb进行AH认证值的计算
err = ah_mac_digest(ahp, skb, ah->auth_data);
if (err)
goto error;
// 赋值初始化向量值到认证数据部分
memcpy(ah->auth_data, ahp->work_icv, ahp->icv_trunc_len);
ahp = x->data;
// AH头长度对齐
ah->hdrlen = (XFRM_ALIGN8(sizeof(struct ip_auth_hdr) +
ahp->icv_trunc_len) >> 2) - 2;
// AH头参数赋值
ah->reserved = 0;
// SPI值
ah->spi = x->id.spi;
// 序列号
ah->seq_no = htonl(++x->replay.oseq);
// 通知防止重放攻击处理, 更新序列号
xfrm_aevent_doreplay(x);
// 对skb进行AH认证值的计算
err = ah_mac_digest(ahp, skb, ah->auth_data);
if (err)
goto error;
// 赋值初始化向量值到认证数据部分
memcpy(ah->auth_data, ahp->work_icv, ahp->icv_trunc_len);
// 恢复原来IP头的的不认证部分的值
top_iph->tos = iph->tos;
top_iph->ttl = iph->ttl;
top_iph->frag_off = iph->frag_off;
if (top_iph->ihl != 5) {
top_iph->daddr = iph->daddr;
memcpy(top_iph+1, iph+1, top_iph->ihl*4 - sizeof(struct iphdr));
}
// 重新计算IP头的认证值
ip_send_check(top_iph);
top_iph->tos = iph->tos;
top_iph->ttl = iph->ttl;
top_iph->frag_off = iph->frag_off;
if (top_iph->ihl != 5) {
top_iph->daddr = iph->daddr;
memcpy(top_iph+1, iph+1, top_iph->ihl*4 - sizeof(struct iphdr));
}
// 重新计算IP头的认证值
ip_send_check(top_iph);
err = 0;
error:
return err;
}
return err;
}
8.2 ESP
8.2.1 初始化
/* net/ipv4/esp4.c */
static int __init esp4_init(void)
{
// 登记ESP协议的xfrm协议处理结构
if (xfrm_register_type(&esp_type, AF_INET) < 0) {
printk(KERN_INFO "ip esp init: can't add xfrm type\n");
return -EAGAIN;
}
// 登记ESP协议到IP协议
if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
printk(KERN_INFO "ip esp init: can't add protocol\n");
xfrm_unregister_type(&esp_type, AF_INET);
return -EAGAIN;
}
return 0;
}
{
// 登记ESP协议的xfrm协议处理结构
if (xfrm_register_type(&esp_type, AF_INET) < 0) {
printk(KERN_INFO "ip esp init: can't add xfrm type\n");
return -EAGAIN;
}
// 登记ESP协议到IP协议
if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
printk(KERN_INFO "ip esp init: can't add protocol\n");
xfrm_unregister_type(&esp_type, AF_INET);
return -EAGAIN;
}
return 0;
}
8.2.2 IPV4下的ESP协议处理结构
// ESP协议处理结构, 接收到IPV4包后, 系统根据IP头中的protocol
// 字段选择相应的上层协议处理函数, 当IP协议号是50时, 数据包将
// 调用该结构的handler处理函数:
static struct net_protocol esp4_protocol = {
.handler = xfrm4_rcv,
.err_handler = esp4_err,
.no_policy = 1,
};
ESP协议结构的handler函数也是xfrm4_rcv, 在net/ipv4/xfrm4_input.c 中定义,
在上一篇中进行了介绍.
// 错误处理, 收到ICMP错误包时的处理情况, 此时的skb包是ICMP包
static void esp4_err(struct sk_buff *skb, u32 info)
{
// 应用层, data指向ICMP错误包里的内部IP头
struct iphdr *iph = (struct iphdr*)skb->data;
// ESP头
struct ip_esp_hdr *esph = (struct ip_esp_hdr*)(skb->data+(iph->ihl<<2));
struct xfrm_state *x;
// ICMP错误类型检查, 本处理函数只处理"目的不可达"和"需要分片"两种错误
if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
// 重新查找SA
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET);
if (!x)
return;
NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n",
ntohl(esph->spi), ntohl(iph->daddr));
xfrm_state_put(x);
}
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET);
if (!x)
return;
NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n",
ntohl(esph->spi), ntohl(iph->daddr));
xfrm_state_put(x);
}
8.2.3 ESP4协议的IPSEC处理结构
static struct xfrm_type esp_type =
{
.description = "ESP4",
.owner = THIS_MODULE,
.proto = IPPROTO_ESP,
// 状态初始化
.init_state = esp_init_state,
// 协议释放
.destructor = esp_destroy,
// 计算最大长度
.get_max_size = esp4_get_max_size,
// 协议输入
.input = esp_input,
// 协议输出
.output = esp_output
};
8.2.3.1 状态初始化
esp_data数据结构:
/* include/net/esp.h */
struct esp_data
{
struct scatterlist sgbuf[ESP_NUM_FAST_SG];
{
struct scatterlist sgbuf[ESP_NUM_FAST_SG];
/* Confidentiality */
// 加密使用的相关数据
struct {
// 密钥
u8 *key; /* Key */
// 密钥长度
int key_len; /* Key length */
// 填充长度
int padlen; /* 0..255 */
/* ivlen is offset from enc_data, where encrypted data start.
* It is logically different of crypto_tfm_alg_ivsize(tfm).
* We assume that it is either zero (no ivec), or
* >= crypto_tfm_alg_ivsize(tfm). */
// 初始化向量长度
int ivlen;
// 初始化向量是否初始化标志
int ivinitted;
// 初始化向量
u8 *ivec; /* ivec buffer */
// 加密算法
struct crypto_blkcipher *tfm; /* crypto handle */
} conf;
// 加密使用的相关数据
struct {
// 密钥
u8 *key; /* Key */
// 密钥长度
int key_len; /* Key length */
// 填充长度
int padlen; /* 0..255 */
/* ivlen is offset from enc_data, where encrypted data start.
* It is logically different of crypto_tfm_alg_ivsize(tfm).
* We assume that it is either zero (no ivec), or
* >= crypto_tfm_alg_ivsize(tfm). */
// 初始化向量长度
int ivlen;
// 初始化向量是否初始化标志
int ivinitted;
// 初始化向量
u8 *ivec; /* ivec buffer */
// 加密算法
struct crypto_blkcipher *tfm; /* crypto handle */
} conf;
/* Integrity. It is active when icv_full_len != 0 */
// 认证使用的相关数据
struct {
// 密钥
u8 *key; /* Key */
// 密钥长度
int key_len; /* Length of the key */
// 初始化向量
u8 *work_icv;
// 初始化向量全长
int icv_full_len;
// 初始化向量截断长度
int icv_trunc_len;
// 初始化向量更新函数, 好象没用
void (*icv)(struct esp_data*,
struct sk_buff *skb,
int offset, int len, u8 *icv);
// HASH算法
struct crypto_hash *tfm;
} auth;
};
// 认证使用的相关数据
struct {
// 密钥
u8 *key; /* Key */
// 密钥长度
int key_len; /* Length of the key */
// 初始化向量
u8 *work_icv;
// 初始化向量全长
int icv_full_len;
// 初始化向量截断长度
int icv_trunc_len;
// 初始化向量更新函数, 好象没用
void (*icv)(struct esp_data*,
struct sk_buff *skb,
int offset, int len, u8 *icv);
// HASH算法
struct crypto_hash *tfm;
} auth;
};
// ESP的esp_data数据结构初始化
static int esp_init_state(struct xfrm_state *x)
{
struct esp_data *esp = NULL;
struct crypto_blkcipher *tfm;
static int esp_init_state(struct xfrm_state *x)
{
struct esp_data *esp = NULL;
struct crypto_blkcipher *tfm;
/* null auth and encryption can have zero length keys */
// 如果有认证算法, 密钥至少512, ESP的认证处理是可选的, 但在实际中都会使用认证
if (x->aalg) {
if (x->aalg->alg_key_len > 512)
goto error;
}
// ESP加密算法是必须的
if (x->ealg == NULL)
goto error;
// 如果有认证算法, 密钥至少512, ESP的认证处理是可选的, 但在实际中都会使用认证
if (x->aalg) {
if (x->aalg->alg_key_len > 512)
goto error;
}
// ESP加密算法是必须的
if (x->ealg == NULL)
goto error;
// 分配esp_data数据结构空间
esp = kzalloc(sizeof(*esp), GFP_KERNEL);
if (esp == NULL)
return -ENOMEM;
// 如果定义了认证算法, 初始化认证算法参数, 和AH类似
if (x->aalg) {
struct xfrm_algo_desc *aalg_desc;
struct crypto_hash *hash;
// 认证密钥和长度设置
esp->auth.key = x->aalg->alg_key;
esp->auth.key_len = (x->aalg->alg_key_len+7)/8;
// 分配HASH算法的实现
hash = crypto_alloc_hash(x->aalg->alg_name, 0,
CRYPTO_ALG_ASYNC);
if (IS_ERR(hash))
goto error;
esp = kzalloc(sizeof(*esp), GFP_KERNEL);
if (esp == NULL)
return -ENOMEM;
// 如果定义了认证算法, 初始化认证算法参数, 和AH类似
if (x->aalg) {
struct xfrm_algo_desc *aalg_desc;
struct crypto_hash *hash;
// 认证密钥和长度设置
esp->auth.key = x->aalg->alg_key;
esp->auth.key_len = (x->aalg->alg_key_len+7)/8;
// 分配HASH算法的实现
hash = crypto_alloc_hash(x->aalg->alg_name, 0,
CRYPTO_ALG_ASYNC);
if (IS_ERR(hash))
goto error;
esp->auth.tfm = hash;
// 设置HASH算法密钥
if (crypto_hash_setkey(hash, esp->auth.key, esp->auth.key_len))
goto error;
// 找到算法描述
aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
BUG_ON(!aalg_desc);
// 检查算法初始化向量长度合法性
if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
crypto_hash_digestsize(hash)) {
NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n",
x->aalg->alg_name,
crypto_hash_digestsize(hash),
aalg_desc->uinfo.auth.icv_fullbits/8);
goto error;
}
// 初始化向量的全长和截断长度
esp->auth.icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
esp->auth.icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
// 分配全长度的初始化向量空间
esp->auth.work_icv = kmalloc(esp->auth.icv_full_len, GFP_KERNEL);
if (!esp->auth.work_icv)
goto error;
}
// 设置HASH算法密钥
if (crypto_hash_setkey(hash, esp->auth.key, esp->auth.key_len))
goto error;
// 找到算法描述
aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
BUG_ON(!aalg_desc);
// 检查算法初始化向量长度合法性
if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
crypto_hash_digestsize(hash)) {
NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n",
x->aalg->alg_name,
crypto_hash_digestsize(hash),
aalg_desc->uinfo.auth.icv_fullbits/8);
goto error;
}
// 初始化向量的全长和截断长度
esp->auth.icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
esp->auth.icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
// 分配全长度的初始化向量空间
esp->auth.work_icv = kmalloc(esp->auth.icv_full_len, GFP_KERNEL);
if (!esp->auth.work_icv)
goto error;
}
// 初始化加密算法相关参数, ESP使用的加密算法都是对称块加密算法, 不可能用非对称算法的
// 加密密钥
esp->conf.key = x->ealg->alg_key;
// 加密密钥长度
esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
// 分配加密算法的具体实现结构
tfm = crypto_alloc_blkcipher(x->ealg->alg_name, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
goto error;
esp->conf.tfm = tfm;
// 初始化向量大小
esp->conf.ivlen = crypto_blkcipher_ivsize(tfm);
// 填充数据长度初始化为0
esp->conf.padlen = 0;
// 初始化向量长度非0, 分配具体的初始化向量空间
if (esp->conf.ivlen) {
esp->conf.ivec = kmalloc(esp->conf.ivlen, GFP_KERNEL);
if (unlikely(esp->conf.ivec == NULL))
goto error;
esp->conf.ivinitted = 0;
}
// 设置加密算法密钥
if (crypto_blkcipher_setkey(tfm, esp->conf.key, esp->conf.key_len))
goto error;
// 定义SA中ESP头部长度: ESP头加初始化向量长度
// 反映在ESP封装操作时要将数据包增加的长度
x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
// 如果是通道模式, 还需要增加IP头长度
if (x->props.mode == XFRM_MODE_TUNNEL)
x->props.header_len += sizeof(struct iphdr);
// 如果要进行UDP封装
if (x->encap) {
struct xfrm_encap_tmpl *encap = x->encap;
// 加密密钥
esp->conf.key = x->ealg->alg_key;
// 加密密钥长度
esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
// 分配加密算法的具体实现结构
tfm = crypto_alloc_blkcipher(x->ealg->alg_name, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
goto error;
esp->conf.tfm = tfm;
// 初始化向量大小
esp->conf.ivlen = crypto_blkcipher_ivsize(tfm);
// 填充数据长度初始化为0
esp->conf.padlen = 0;
// 初始化向量长度非0, 分配具体的初始化向量空间
if (esp->conf.ivlen) {
esp->conf.ivec = kmalloc(esp->conf.ivlen, GFP_KERNEL);
if (unlikely(esp->conf.ivec == NULL))
goto error;
esp->conf.ivinitted = 0;
}
// 设置加密算法密钥
if (crypto_blkcipher_setkey(tfm, esp->conf.key, esp->conf.key_len))
goto error;
// 定义SA中ESP头部长度: ESP头加初始化向量长度
// 反映在ESP封装操作时要将数据包增加的长度
x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
// 如果是通道模式, 还需要增加IP头长度
if (x->props.mode == XFRM_MODE_TUNNEL)
x->props.header_len += sizeof(struct iphdr);
// 如果要进行UDP封装
if (x->encap) {
struct xfrm_encap_tmpl *encap = x->encap;
switch (encap->encap_type) {
default:
goto error;
case UDP_ENCAP_ESPINUDP:
// 该类型封装增加UDP头长度
x->props.header_len += sizeof(struct udphdr);
break;
case UDP_ENCAP_ESPINUDP_NON_IKE:
// 该类型封装增加UDP头长度外加加8字节
x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32);
break;
}
}
// 将esp_data作为SA的data指针
x->data = esp;
// 追踪长度, 最大增加长度和当前的计算的增加长度的差值,在路由时会用到
// 对于AH, 由于没有定义get_max_size(), 该值位0
x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len;
return 0;
default:
goto error;
case UDP_ENCAP_ESPINUDP:
// 该类型封装增加UDP头长度
x->props.header_len += sizeof(struct udphdr);
break;
case UDP_ENCAP_ESPINUDP_NON_IKE:
// 该类型封装增加UDP头长度外加加8字节
x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32);
break;
}
}
// 将esp_data作为SA的data指针
x->data = esp;
// 追踪长度, 最大增加长度和当前的计算的增加长度的差值,在路由时会用到
// 对于AH, 由于没有定义get_max_size(), 该值位0
x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len;
return 0;
error:
x->data = esp;
esp_destroy(x);
x->data = NULL;
return -EINVAL;
}
x->data = esp;
esp_destroy(x);
x->data = NULL;
return -EINVAL;
}
8.2.3.2 协议释放
// 该函数被xfrm状态(SA)释放函数xfrm_state_gc_destroy()调用
static void esp_destroy(struct xfrm_state *x)
{
struct esp_data *esp = x->data;
if (!esp)
return;
// 释放加密算法
crypto_free_blkcipher(esp->conf.tfm);
esp->conf.tfm = NULL;
// 释放加密初始化向量
kfree(esp->conf.ivec);
esp->conf.ivec = NULL;
// 释放认证算法
crypto_free_hash(esp->auth.tfm);
esp->auth.tfm = NULL;
// 释放认证初始化向量
kfree(esp->auth.work_icv);
esp->auth.work_icv = NULL;
// 释放esp_data
kfree(esp);
}
return;
// 释放加密算法
crypto_free_blkcipher(esp->conf.tfm);
esp->conf.tfm = NULL;
// 释放加密初始化向量
kfree(esp->conf.ivec);
esp->conf.ivec = NULL;
// 释放认证算法
crypto_free_hash(esp->auth.tfm);
esp->auth.tfm = NULL;
// 释放认证初始化向量
kfree(esp->auth.work_icv);
esp->auth.work_icv = NULL;
// 释放esp_data
kfree(esp);
}
8.2.3.3 计算最大长度
// 在xfrm_state_mtu()函数中调用, 计算最大增加的数据长度
// AH中没有该函数, 增加的长度使用x->props.header_len
static u32 esp4_get_max_size(struct xfrm_state *x, int mtu)
{
struct esp_data *esp = x->data;
// 加密块长度, 按4字节对齐
u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4);
int enclen = 0;
switch (x->props.mode) {
case XFRM_MODE_TUNNEL:
// 通道模式下的MTU, 按加密块大小对齐, +2是要包括2字节数据长度
mtu = ALIGN(mtu +2, blksize);
break;
default:
case XFRM_MODE_TRANSPORT:
/* The worst case */
// 传输模式下, MTU先按4字节对齐, 再加块长度减4
mtu = ALIGN(mtu + 2, 4) + blksize - 4;
break;
case XFRM_MODE_BEET:
/* The worst case. */
enclen = IPV4_BEET_PHMAXLEN;
mtu = ALIGN(mtu + enclen + 2, blksize);
break;
}
// 如果加密算法中定义了填充长度, MTU也要按填充长度对齐
if (esp->conf.padlen)
mtu = ALIGN(mtu, esp->conf.padlen);
// 返回MTU加提议中需要增加的头部长度和认证初始化向量的截断长度
// enclen只在BEET模式下非0, 在通道和传输模式下都是0
return mtu + x->props.header_len + esp->auth.icv_trunc_len - enclen;
}
case XFRM_MODE_TUNNEL:
// 通道模式下的MTU, 按加密块大小对齐, +2是要包括2字节数据长度
mtu = ALIGN(mtu +2, blksize);
break;
default:
case XFRM_MODE_TRANSPORT:
/* The worst case */
// 传输模式下, MTU先按4字节对齐, 再加块长度减4
mtu = ALIGN(mtu + 2, 4) + blksize - 4;
break;
case XFRM_MODE_BEET:
/* The worst case. */
enclen = IPV4_BEET_PHMAXLEN;
mtu = ALIGN(mtu + enclen + 2, blksize);
break;
}
// 如果加密算法中定义了填充长度, MTU也要按填充长度对齐
if (esp->conf.padlen)
mtu = ALIGN(mtu, esp->conf.padlen);
// 返回MTU加提议中需要增加的头部长度和认证初始化向量的截断长度
// enclen只在BEET模式下非0, 在通道和传输模式下都是0
return mtu + x->props.header_len + esp->auth.icv_trunc_len - enclen;
}
8.2.3.4 协议输入
struct scatterlist结构说明:
/* include/asm-i386/scatterlist.h */
struct scatterlist {
struct page *page;
unsigned int offset;
dma_addr_t dma_address;
unsigned int length;
};
struct page *page;
unsigned int offset;
dma_addr_t dma_address;
unsigned int length;
};
/*
* Note: detecting truncated vs. non-truncated authentication data is very
* expensive, so we only support truncated data, which is the recommended
* and common case.
*/
// 接收数据处理, 在xfrm4_rcv_encap()函数中调用
// 进行ESP认证解密, 剥离ESP头, 解密成普通数据包, 数据包长度减少
// 输入的数据包是ESP包
static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
{
struct iphdr *iph;
struct ip_esp_hdr *esph;
struct esp_data *esp = x->data;
struct crypto_blkcipher *tfm = esp->conf.tfm;
struct blkcipher_desc desc = { .tfm = tfm };
struct sk_buff *trailer;
int blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4);
// 认证初始化向量截断长度
int alen = esp->auth.icv_trunc_len;
// 需要加密的数据长度: 总长减ESP头, 加密初始化向量长度, 认证初始化向量长度
int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen;
int nfrags;
int ihl;
u8 nexthdr[2];
struct scatterlist *sg;
int padlen;
int err;
// 在skb头留出ESP头的空间
if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
goto out;
if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
goto out;
// 检查需要加密的数据长度, 必须大于0而且按块大小对齐的
if (elen <= 0 || (elen & (blksize-1)))
goto out;
if (elen <= 0 || (elen & (blksize-1)))
goto out;
/* If integrity check is required, do this. */
// 认证计算处理
if (esp->auth.icv_full_len) {
u8 sum[alen];
// 计算认证值, 认证值保存在esp_data结构中
err = esp_mac_digest(esp, skb, 0, skb->len - alen);
if (err)
goto out;
// 认证计算处理
if (esp->auth.icv_full_len) {
u8 sum[alen];
// 计算认证值, 认证值保存在esp_data结构中
err = esp_mac_digest(esp, skb, 0, skb->len - alen);
if (err)
goto out;
// 将skb中的认证初始化向量部分数据拷贝到缓冲区sum中
if (skb_copy_bits(skb, skb->len - alen, sum, alen))
BUG();
// 比较sum中的向量值和认证算法结构中的向量值是否匹配, 数据包正常情况下应该是相同的
if (unlikely(memcmp(esp->auth.work_icv, sum, alen))) {
x->stats.integrity_failed++;
goto out;
}
}
// 使数据包是可写的
if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0)
goto out;
if (skb_copy_bits(skb, skb->len - alen, sum, alen))
BUG();
// 比较sum中的向量值和认证算法结构中的向量值是否匹配, 数据包正常情况下应该是相同的
if (unlikely(memcmp(esp->auth.work_icv, sum, alen))) {
x->stats.integrity_failed++;
goto out;
}
}
// 使数据包是可写的
if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0)
goto out;
skb->ip_summed = CHECKSUM_NONE;
// 定位在数据包中的ESP头位置, 为当前的data位置
esph = (struct ip_esp_hdr*)skb->data;
// 定位在数据包中的ESP头位置, 为当前的data位置
esph = (struct ip_esp_hdr*)skb->data;
/* Get ivec. This can be wrong, check against another impls. */
// 设置加密算法的初始化向量
if (esp->conf.ivlen)
crypto_blkcipher_set_iv(tfm, esph->enc_data, esp->conf.ivlen);
// 设置加密算法的初始化向量
if (esp->conf.ivlen)
crypto_blkcipher_set_iv(tfm, esph->enc_data, esp->conf.ivlen);
sg = &esp->sgbuf[0];
if (unlikely(nfrags > ESP_NUM_FAST_SG)) {
sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
if (!sg)
goto out;
}
skb_to_sgvec(skb, sg, sizeof(struct ip_esp_hdr) + esp->conf.ivlen, elen);
// 解密操作, 返回非0表示失败
err = crypto_blkcipher_decrypt(&desc, sg, sg, elen);
if (unlikely(sg != &esp->sgbuf[0]))
kfree(sg);
// 解密失败返回
if (unlikely(err))
return err;
// 拷贝两字节数据
if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))
BUG();
sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
if (!sg)
goto out;
}
skb_to_sgvec(skb, sg, sizeof(struct ip_esp_hdr) + esp->conf.ivlen, elen);
// 解密操作, 返回非0表示失败
err = crypto_blkcipher_decrypt(&desc, sg, sg, elen);
if (unlikely(sg != &esp->sgbuf[0]))
kfree(sg);
// 解密失败返回
if (unlikely(err))
return err;
// 拷贝两字节数据
if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))
BUG();
padlen = nexthdr[0];
if (padlen+2 >= elen)
goto out;
if (padlen+2 >= elen)
goto out;
/* ... check padding bits here. Silly. :-) */
// 新的IP头
iph = skb->nh.iph;
ihl = iph->ihl * 4;
// 如果是NAT穿越情况, 进行一些处理
if (x->encap) {
// xfrm封装模板
struct xfrm_encap_tmpl *encap = x->encap;
// 定位UDP数据头位置, 在IP头之后
struct udphdr *uh = (void *)(skb->nh.raw + ihl);
// 新的IP头
iph = skb->nh.iph;
ihl = iph->ihl * 4;
// 如果是NAT穿越情况, 进行一些处理
if (x->encap) {
// xfrm封装模板
struct xfrm_encap_tmpl *encap = x->encap;
// 定位UDP数据头位置, 在IP头之后
struct udphdr *uh = (void *)(skb->nh.raw + ihl);
/*
* 1) if the NAT-T peer's IP or port changed then
* advertize the change to the keying daemon.
* This is an inbound SA, so just compare
* SRC ports.
*/
// 如果IP头源地址和SA提议中的源地址不同或源端口不同
if (iph->saddr != x->props.saddr.a4 ||
uh->source != encap->encap_sport) {
xfrm_address_t ipaddr;
// 保存当前IP头源地址
ipaddr.a4 = iph->saddr;
// 进行NAT通知回调处理
km_new_mapping(x, &ipaddr, uh->source);
/* XXX: perhaps add an extra
* policy check here, to see
* if we should allow or
* reject a packet from a
* different source
* address/port.
*/
}
/*
* 2) ignore UDP/TCP checksums in case
* of NAT-T in Transport Mode, or
* perform other post-processing fixes
* as per draft-ietf-ipsec-udp-encaps-06,
* section 3.1.2
*/
// 如果是传输模式或BEET模式, 设置不需要计算校验和
if (x->props.mode == XFRM_MODE_TRANSPORT ||
x->props.mode == XFRM_MODE_BEET)
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
// 新IP头中协议
iph->protocol = nexthdr[1];
// 缩减skb数据包长度
pskb_trim(skb, skb->len - alen - padlen - 2);
// 重新定位IP上层数据头位置
skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - ihl;
* 1) if the NAT-T peer's IP or port changed then
* advertize the change to the keying daemon.
* This is an inbound SA, so just compare
* SRC ports.
*/
// 如果IP头源地址和SA提议中的源地址不同或源端口不同
if (iph->saddr != x->props.saddr.a4 ||
uh->source != encap->encap_sport) {
xfrm_address_t ipaddr;
// 保存当前IP头源地址
ipaddr.a4 = iph->saddr;
// 进行NAT通知回调处理
km_new_mapping(x, &ipaddr, uh->source);
/* XXX: perhaps add an extra
* policy check here, to see
* if we should allow or
* reject a packet from a
* different source
* address/port.
*/
}
/*
* 2) ignore UDP/TCP checksums in case
* of NAT-T in Transport Mode, or
* perform other post-processing fixes
* as per draft-ietf-ipsec-udp-encaps-06,
* section 3.1.2
*/
// 如果是传输模式或BEET模式, 设置不需要计算校验和
if (x->props.mode == XFRM_MODE_TRANSPORT ||
x->props.mode == XFRM_MODE_BEET)
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
// 新IP头中协议
iph->protocol = nexthdr[1];
// 缩减skb数据包长度
pskb_trim(skb, skb->len - alen - padlen - 2);
// 重新定位IP上层数据头位置
skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - ihl;
return 0;
out:
return -EINVAL;
}
return -EINVAL;
}
8.2.3.4 协议输出
// 发送数据处理, 在xfrm4_output_one()中调用
// 添加ESP头, 对数据包进行加密和认证处理, 数据包长度扩大
// 在NAT穿越情况下会封装为UDP数据
static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
struct iphdr *top_iph;
struct ip_esp_hdr *esph;
struct crypto_blkcipher *tfm;
struct blkcipher_desc desc;
struct esp_data *esp;
struct sk_buff *trailer;
int blksize;
int clen;
int alen;
int nfrags;
/* Strip IP+ESP header. */
// 缩减skb数据, 减去IP头和ESP头, 剩下的数据就是要进行加密和认证的部分
__skb_pull(skb, skb->h.raw - skb->data);
/* Now skb is pure payload to encrypt */
// 缩减skb数据, 减去IP头和ESP头, 剩下的数据就是要进行加密和认证的部分
__skb_pull(skb, skb->h.raw - skb->data);
/* Now skb is pure payload to encrypt */
err = -ENOMEM;
/* Round to block size */
// 加密块的初始值
clen = skb->len;
// 加密块的初始值
clen = skb->len;
// 获取SA的esp_data数据结构
esp = x->data;
// 认证初始化向量截断长度
alen = esp->auth.icv_trunc_len;
// 加密算法
tfm = esp->conf.tfm;
// 给块加密算法描述结构赋值
desc.tfm = tfm;
desc.flags = 0;
// 每个加密块大小
blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4);
// 对齐要加密的数据总长
clen = ALIGN(clen + 2, blksize);
// 如果要考虑填充, 继续对齐
if (esp->conf.padlen)
clen = ALIGN(clen, esp->conf.padlen);
esp = x->data;
// 认证初始化向量截断长度
alen = esp->auth.icv_trunc_len;
// 加密算法
tfm = esp->conf.tfm;
// 给块加密算法描述结构赋值
desc.tfm = tfm;
desc.flags = 0;
// 每个加密块大小
blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4);
// 对齐要加密的数据总长
clen = ALIGN(clen + 2, blksize);
// 如果要考虑填充, 继续对齐
if (esp->conf.padlen)
clen = ALIGN(clen, esp->conf.padlen);
// 使数据包可写
if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0)
goto error;
if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0)
goto error;
/* Fill padding... */
// 长度对齐后填充多余长度部分内容
do {
int i;
for (i=0; i<clen-skb->len - 2; i++)
*(u8*)(trailer->tail + i) = i+1;
} while (0);
// 最后两字节表示填充数据的长度
*(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
pskb_put(skb, trailer, clen - skb->len);
// 在将IP头部分扩展回来
__skb_push(skb, skb->data - skb->nh.raw);
// 现在的IP头作为外部IP头
top_iph = skb->nh.iph;
// esp头跟在IP头后
esph = (struct ip_esp_hdr *)(skb->nh.raw + top_iph->ihl*4);
// 数据总长增加认证部分长度
top_iph->tot_len = htons(skb->len + alen);
*(u8*)(trailer->tail - 1) = top_iph->protocol;
// 长度对齐后填充多余长度部分内容
do {
int i;
for (i=0; i<clen-skb->len - 2; i++)
*(u8*)(trailer->tail + i) = i+1;
} while (0);
// 最后两字节表示填充数据的长度
*(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
pskb_put(skb, trailer, clen - skb->len);
// 在将IP头部分扩展回来
__skb_push(skb, skb->data - skb->nh.raw);
// 现在的IP头作为外部IP头
top_iph = skb->nh.iph;
// esp头跟在IP头后
esph = (struct ip_esp_hdr *)(skb->nh.raw + top_iph->ihl*4);
// 数据总长增加认证部分长度
top_iph->tot_len = htons(skb->len + alen);
*(u8*)(trailer->tail - 1) = top_iph->protocol;
/* this is non-NULL only with UDP Encapsulation */
if (x->encap) {
// NAT穿越情况下要将数据封装为UDP包
struct xfrm_encap_tmpl *encap = x->encap;
struct udphdr *uh;
u32 *udpdata32;
// IP头后改为UDP头
uh = (struct udphdr *)esph;
// 填充UDP头参数, 源端口, 目的端口, UDP数据长度
uh->source = encap->encap_sport;
uh->dest = encap->encap_dport;
uh->len = htons(skb->len + alen - top_iph->ihl*4);
// 校验和为0, 表示不需要计算校验和, ESP本身就进行认证了
uh->check = 0;
if (x->encap) {
// NAT穿越情况下要将数据封装为UDP包
struct xfrm_encap_tmpl *encap = x->encap;
struct udphdr *uh;
u32 *udpdata32;
// IP头后改为UDP头
uh = (struct udphdr *)esph;
// 填充UDP头参数, 源端口, 目的端口, UDP数据长度
uh->source = encap->encap_sport;
uh->dest = encap->encap_dport;
uh->len = htons(skb->len + alen - top_iph->ihl*4);
// 校验和为0, 表示不需要计算校验和, ESP本身就进行认证了
uh->check = 0;
switch (encap->encap_type) {
default:
case UDP_ENCAP_ESPINUDP:
// 在该模式下ESP头跟在UDP头后面
esph = (struct ip_esp_hdr *)(uh + 1);
break;
case UDP_ENCAP_ESPINUDP_NON_IKE:
// 在该模式下ESP头跟在UDP头后面8字节处
udpdata32 = (u32 *)(uh + 1);
udpdata32[0] = udpdata32[1] = 0;
esph = (struct ip_esp_hdr *)(udpdata32 + 2);
break;
}
// 外部IP头协议是UDP
top_iph->protocol = IPPROTO_UDP;
} else
// 非NAT穿越情况下, 外部IP头中的协议是ESP
top_iph->protocol = IPPROTO_ESP;
default:
case UDP_ENCAP_ESPINUDP:
// 在该模式下ESP头跟在UDP头后面
esph = (struct ip_esp_hdr *)(uh + 1);
break;
case UDP_ENCAP_ESPINUDP_NON_IKE:
// 在该模式下ESP头跟在UDP头后面8字节处
udpdata32 = (u32 *)(uh + 1);
udpdata32[0] = udpdata32[1] = 0;
esph = (struct ip_esp_hdr *)(udpdata32 + 2);
break;
}
// 外部IP头协议是UDP
top_iph->protocol = IPPROTO_UDP;
} else
// 非NAT穿越情况下, 外部IP头中的协议是ESP
top_iph->protocol = IPPROTO_ESP;
// 填充ESP头中的SPI和序列号
esph->spi = x->id.spi;
esph->seq_no = htonl(++x->replay.oseq);
// 序列号更新通知回调
xfrm_aevent_doreplay(x);
esph->spi = x->id.spi;
esph->seq_no = htonl(++x->replay.oseq);
// 序列号更新通知回调
xfrm_aevent_doreplay(x);
// 如果加密初始化向量长度非零, 设置加密算法中的初始化向量
if (esp->conf.ivlen) {
if (unlikely(!esp->conf.ivinitted)) {
get_random_bytes(esp->conf.ivec, esp->conf.ivlen);
esp->conf.ivinitted = 1;
}
crypto_blkcipher_set_iv(tfm, esp->conf.ivec, esp->conf.ivlen);
}
// 加密操作
do {
struct scatterlist *sg = &esp->sgbuf[0];
if (esp->conf.ivlen) {
if (unlikely(!esp->conf.ivinitted)) {
get_random_bytes(esp->conf.ivec, esp->conf.ivlen);
esp->conf.ivinitted = 1;
}
crypto_blkcipher_set_iv(tfm, esp->conf.ivec, esp->conf.ivlen);
}
// 加密操作
do {
struct scatterlist *sg = &esp->sgbuf[0];
if (unlikely(nfrags > ESP_NUM_FAST_SG)) {
sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
if (!sg)
goto error;
}
skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
// 对数据加密
err = crypto_blkcipher_encrypt(&desc, sg, sg, clen);
if (unlikely(sg != &esp->sgbuf[0]))
kfree(sg);
} while (0);
sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
if (!sg)
goto error;
}
skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
// 对数据加密
err = crypto_blkcipher_encrypt(&desc, sg, sg, clen);
if (unlikely(sg != &esp->sgbuf[0]))
kfree(sg);
} while (0);
if (unlikely(err))
goto error;
// 将加密算法初始化向量拷贝到数据包
if (esp->conf.ivlen) {
memcpy(esph->enc_data, esp->conf.ivec, esp->conf.ivlen);
crypto_blkcipher_get_iv(tfm, esp->conf.ivec, esp->conf.ivlen);
}
// 认证计算, 计算出HASH值并拷贝到数据包中
if (esp->auth.icv_full_len) {
err = esp_mac_digest(esp, skb, (u8 *)esph - skb->data,
sizeof(*esph) + esp->conf.ivlen + clen);
memcpy(pskb_put(skb, trailer, alen), esp->auth.work_icv, alen);
}
// 重新计算外部IP头校验和
ip_send_check(top_iph);
goto error;
// 将加密算法初始化向量拷贝到数据包
if (esp->conf.ivlen) {
memcpy(esph->enc_data, esp->conf.ivec, esp->conf.ivlen);
crypto_blkcipher_get_iv(tfm, esp->conf.ivec, esp->conf.ivlen);
}
// 认证计算, 计算出HASH值并拷贝到数据包中
if (esp->auth.icv_full_len) {
err = esp_mac_digest(esp, skb, (u8 *)esph - skb->data,
sizeof(*esph) + esp->conf.ivlen + clen);
memcpy(pskb_put(skb, trailer, alen), esp->auth.work_icv, alen);
}
// 重新计算外部IP头校验和
ip_send_check(top_iph);
error:
return err;
}
return err;
}
...... 待续 ......
发表评论
-
Linux发送函数dev_queue_xmit分析 --转
2010-12-14 21:44 14901当上层准备好一个包之后,交给下面这个函数处理: int de ... -
内核污染错误
2010-12-12 21:48 1860一些oops报告在程序记数器之后包含字符串'Tainted: ... -
EXPORT_SYMBOL 与 EXPORT_SYMBOL_GPL 转载
2010-12-12 15:04 121961.EXPORT_SYMBOL EXPORT_SYMB ... -
TCP和UDP在网络层实现的不同--基于linux内核 --转
2010-12-11 20:01 1363由于4层协议实 ... -
ipv6 分片
2010-12-07 21:13 5118519 static int ip6_fragment(str ... -
ipv6 处理扩展头
2010-12-06 09:47 3604160 static int ip6_input_finish ... -
IPV6详解 --转
2010-12-02 14:11 1237一、IPv6基本头 IPv6基本头标包含40字节 ... -
Linux内核中的IPSEC实现(7) ---转载
2010-11-16 20:19 4085本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中的IPSEC实现(5) ---转载
2010-11-15 15:48 4094本文档的Copyleft归yfydz所有,使用GPL发布 ... -
Linux内核中的IPSEC实现(4) ---转载
2010-11-15 15:46 1442本文档的Copyleft归yfydz ... -
Linux内核中的IPSEC实现(3) ---转载
2010-11-15 15:42 1633本文档的Copyleft归yfydz所有,使用GPL发布,可 ... -
Linux内核中的IPSEC实现(2) ---转载
2010-11-15 15:35 2902本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中的IPSEC实现(1) ---转载
2010-11-15 15:31 2778本文档的Copyleft归yfydz所有,使用GPL发布,可以 ...
相关推荐
总结以上知识点,本文对Linux内核加密框架从理论和实践两个层面进行了深入探讨,尤其重点介绍了算法模版的概念、使用方法,以及IPSec协议中的应用实例。此外,还提到了Linux内核版本相关信息和对文章内容的使用声明...
芋道 yudao ruoyi-vue-pro pay sql , 更新时间 2024-08 ,可对应yudao版本2.4.1
图像处理项目实战
中国民营科技促进会2023中国大数据企业排行榜V8.0大数据产业白皮书110页.pdf
系统选用B/S模式,后端应用springboot框架,前端应用vue框架, MySQL为后台数据库。 本系统基于java设计的各项功能,数据库服务器端采用了Mysql作为后台数据库,使Web与数据库紧密联系起来。 在设计过程中,充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。
图像处理项目实战
一周入门 UE 开发教程背景知识
车牌识别项目
目录: 第 1 章:Ansible 自动化部署 K8S 集群【内含6个文件】 第 2 章:弹性伸缩【内含4个文件】 第 3 章:Helm 应用包管理器(v3)【内含3个文件】 第 4 章:K8S 集群网络【内含1个文件】 第 5 章:K8S 存储 之 Ceph 分布式存储系统【内含4个文件】 第 6 章:微服务容器化迁移【内含3个文件】 第 7 章:基于 K8S 构建企业 Jenkins CICD 平台(微服务)【内含4个文件】 第 8 章:服务治理 Istio 初探【内含1个文件】 网盘文件永久链接
第一章 绪 论 1 1.1背景及意义 1 1.2国内外研究概况 2 1.3 研究的内容 2 第二章 关键技术的研究 3 2.1 相关技术 3 2.2 Java技术 3 2.3 ECLIPSE 开发环境 4 2.4 Tomcat介绍 4 2.5 Spring Boot框架 5 第三章 系统分析 5 3.1 系统设计目标 6 3.2 系统可行性分析 6 3.3 系统功能分析和描述 7 3.4系统UML用例分析 8 3.4.1管理员用例 9 3.4.2用户用例 9 3.5系统流程分析 10 3.5.1添加信息流程 11 3.5.2操作流程 12 3.5.3删除信息流程 13 第四章 系统设计 14 4.1 系统体系结构 15 4.2 数据库设计原则 16 4.3 数据表 17 第五章 系统实现 18 5.1用户功能模块 18 5.2管理员功能模块 19 5.3美食店功能模块 19 5.4前台首页功能模块 19 第六章 系统测试 20 6.1测试定义及目的 21 6.2性能测试 22 6.3测试模块 23 6.4测试结果 24 总 结 26 致 谢 27 参考文献 28
33559399361500导师评价.zip
前端后端修改改进第一次
网络安全是通过技术手段、管理策略和法律规范,保护网络系统、数据及用户隐私免受未经授权的访问、攻击或泄露,确保网络服务的可用性、数据的完整性和机密性。其核心内容包括: 1. 技术防护:如防火墙、入侵检测系统(IDS)、加密通信、漏洞修复等。 2. 管理措施:包括安全策略制定、访问控制、安全审计、应急响应等。 3. 法律与合规:遵循《网络安全法》《数据安全法》等法规,保障用户隐私与国家安全。 4. 安全意识:提升用户对钓鱼攻击、密码安全等风险的防范能力。 # 适用人群 - 企业/组织:IT运维人员、安全管理员、开发工程师(需保障业务系统安全)。 - 普通用户:需防范个人信息泄露、网络诈骗等风险。 - 政府与公共部门:确保关键基础设施(如电力、金融、通信)的安全运行。 - 教育领域:学生及教师需了解基础安全知识以应对网络威胁。 # 适用场景及目标 1. 企业场景: - 目标:防御黑客攻击、数据泄露、勒索软件等,保障业务连续性。 - 措施:部署网络隔离、多因素认证、定期渗透测试。 2
需要将源码中的key换成正式的key方可使用
幼儿园教师儿童行为观察:困惑与思考
多模态大语言模型(MLLMs)具备视频理解能力,正在迅速发展。为了有效评估其视频理解能力,提出了针对长视频理解的基准测试,如Video-MME和MLVU。然而,这些基准直接使用均匀帧采样进行测试,导致信息大量丢失,影响了评估结果对MLLMs真实能力的准确性。为此,我们提出了一种称为RAG-Adapter的即插即用框架,通过采样与给定问题最相关的帧来减少测试期间的信息损失。此外,我们引入了一种分组监督对比学习(GCL)方法,以进一步通过在我们构建的MMAT数据集上的微调来提高RAG-Adapter的采样效果。最后,我们在各种视频理解基准上测试了多个基线MLLMs,发现RAG-Adapter采样始终优于均匀采样(例如,GPT-4o在Video-MME上的准确率提高了9.3%),为长视频基准提供了一种更准确的测试方法。
在当今数字化浪潮中,园区智慧化建设正成为推动区域经济发展和产业转型升级的关键力量。这份园区智慧化解决方案全面展示了如何通过集成大数据、云计算、物联网(IoT)、人工智能(AI)、地理信息系统(GIS)和建筑信息模型(BIM)等前沿技术,为传统产业园区插上数字的翅膀,打造“数字创新”产业园区。 数字技术赋能,重塑园区生态 传统产业园区往往面临运营效率低下、管理粗放、资源利用率不高等问题。而通过智慧化改造,园区可以实现从“清水房”到“精装房”的华丽蜕变。数字化技术不仅提升了园区的运营管理水平,降低了运营成本,还显著增强了园区的竞争力和吸引力。例如,通过构建园区数字模型(CIM),实现了多规数据融合,形成了园区规划“一张图”,为园区管理提供了直观、高效的可视化工具。此外,智能感知设施的应用,如环境监测、能耗监测等,让园区管理更加精细化、科学化。智慧能源管理系统通过实时监测和智能分析,帮助园区实现低碳绿色发展,而综合安防管控系统则通过AI+视频融合技术,为园区安全保驾护航。更有趣的是,这些技术的应用还让园区服务变得更加个性化和便捷,比如园区移动APP,让企业和员工可以随时随地享受园区服务,从会议室预定到智慧公寓管理,一切尽在“掌”握。 智慧运营中心,打造园区大脑 园区智慧化建设的核心在于构建智慧运营中心,这可以看作是园区的“数字大脑”。通过集成物联网服务平台、大数据分析平台、应用开发赋能平台等核心支撑平台,智慧运营中心实现了对园区内各类数据的实时采集、处理和分析。在这个“大脑”的指挥下,园区管理变得更加高效、协同。比如,建设工程项目智慧监管系统,通过基于二三维GIS底图的统一数字化监管,实现了对园区在建工程项目的进度控制、质量控制和安全控制的全方位监管。可视化招商系统则利用CIM模型,以多种方式为园区对外招商推介提供了数字化、在线化的展示窗口。而产业经济分析系统,则通过挖掘和分析产业数据,为园区产业发展提供了有力的决策支持。智慧运营中心的建设,不仅提升了园区的整体运营水平,还为园区的可持续发展奠定了坚实基础。 产业服务升级,激发创新活力 园区智慧化建设不仅关注基础设施和运营管理的升级,更重视产业服务的创新。通过整合平台资源、园区本地资源和外围资源,打造园区服务资源池,为园区内的企业和个人提供了全面的智慧管理、智慧工作和智慧生活服务。特别是工业互联网平台和工业云服务的建设,为园区内的企业提供了轻量化、智能化的生产服务。这些服务涵盖了车间信息化管理、云制造执行、云智能仓储、设备健康管理等多个方面,有效提升了企业的生产效率和竞争力。此外,通过产业经济分析系统,园区还能够对潜在客户进行挖掘、对经销商进行风控、对产品销量进行预测等,为企业的市场营销提供了有力支持。这些创新的产业服务,不仅激发了园区的创新活力,还为区域经济的转型升级注入了新的动力。总之,园区智慧化建设是一场深刻的变革,它正以前所未有的方式重塑着园区的生态、运营和服务模式,为园区的可持续发展开辟了广阔的前景。
python、yolo、pytorch
chromedriver-mac-x64-136.0.7064.0.zip
图像处理项目实战