linux内核中的ipsec实现6转载.docx
- 文档编号:5403946
- 上传时间:2022-12-16
- 格式:DOCX
- 页数:30
- 大小:27.90KB
linux内核中的ipsec实现6转载.docx
《linux内核中的ipsec实现6转载.docx》由会员分享,可在线阅读,更多相关《linux内核中的ipsec实现6转载.docx(30页珍藏版)》请在冰豆网上搜索。
linux内核中的ipsec实现6转载
linux内核中的ipsec实现(6)--转载
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中,
每个协议实现实际是要完成两个结构:
structnet_protocol和structxfrm_type,
前者用于处理接收的该协议类型的IP包,后者则是IPSEC协议处理.8.1AH8.1.1初始化/*net/ipv4/ah4.c*/
staticint__initah4_init(void)
{
//登记AH协议的xfrm协议处理结构
if(xfrm_register_type(&ah_type,AF_INET)<0){
printk(KERN_INFO"ipahinit:
can'taddxfrmtype\n");
return-EAGAIN;
}
//登记AH协议到IP协议
if(inet_add_protocol(&ah4_protocol,IPPROTO_AH)<0){
printk(KERN_INFO"ipahinit:
can'taddprotocol\n");
xfrm_unregister_type(&ah_type,AF_INET);
return-EAGAIN;
}
return0;
}8.1.2IPV4下的AH协议处理结构//AH协议处理结构,接收到IPV4包后,系统根据IP头中的protocol字段选择相应的上层协议处理
//函数,当IP协议号是51时,数据包将调用该结构的handler处理函数:
staticstructnet_protocolah4_protocol={
.handler=xfrm4_rcv,
.err_handler=ah4_err,
.no_policy=1,
};
AH协议结构的handler函数为xfrm4_rcv,在net/ipv4/xfrm4_input.c中定义,在上一篇中进行了介绍.//错误处理,收到ICMP错误包时的处理情况,此时的skb包是ICMP包
staticvoidah4_err(structsk_buff*skb,u32info)
{
//应用层,data指向ICMP错误包里的内部IP头
structiphdr*iph=(structiphdr*)skb->data;
//AH头
structip_auth_hdr*ah=(structip_auth_hdr*)(skb->data+(iph->ihl<<2));
structxfrm_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"pmtudiscoveryonSAAH/%08x/%08x\n",
ntohl(ah->spi),ntohl(iph->daddr));
xfrm_state_put(x);
}8.1.3AH4协议的IPSEC处理结构//AH4的xfrm协议处理结构
staticstructxfrm_typeah_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*/
structah_data
{
//密钥指针
u8*key;
//密钥长度
intkey_len;
//工作初始化向量
u8*work_icv;
//初始化向量完整长度
inticv_full_len;
//初始化向量截断长度
inticv_trunc_len;
//HASH算法
structcrypto_hash*tfm;
};//该函数被xfrm状态(SA)初始化函数xfrm_init_state调用
//用来生成SA中所用的AH数据处理结构相关信息
staticintah_init_state(structxfrm_state*x)
{
structah_data*ahp=NULL;
structxfrm_algo_desc*aalg_desc;
structcrypto_hash*tfm;
//对AH协议的SA,认证算法是必须的,否则就没法进行AH认证了
if(!
x->aalg)
gotoerror;
/*nullauthcanuseazerolengthkey*/
//认证算法密钥长度要大于512
if(x->aalg->alg_key_len>512)
gotoerror;
//如果要进行UDP封装(进行NAT穿越),错误,因为AH是不支持NAT的
if(x->encap)
gotoerror;
//分配ah_data数据结构空间
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))
gotoerror;
ahp->tfm=tfm;
//设置认证算法密钥
if(crypto_hash_setkey(tfm,ahp->key,ahp->key_len))
gotoerror;
/*
*Lookupthealgorithmdescriptionmaintainedbyxfrm_algo,
*verifycryptotransformproperties,andstoreinformation
*weneedforAHprocessing.Thislookupcannotfailhere
*afterasuccessfulcrypto_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:
%sdigestsize%u!
=%hu\n",
x->aalg->alg_name,crypto_hash_digestsize(tfm),
aalg_desc->uinfo.auth.icv_fullbits/8);
gotoerror;
}
//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)
gotoerror;
//AH类型SA中AH头长度:
ip_auth_hdr结构和初始化向量长度,按8字节对齐
//反映在AH封装操作时要将数据包增加的长度
x->props.header_len=XFRM_ALIGN8(sizeof(structip_auth_hdr)+ahp->icv_trunc_len);
//如果是通道模式,增加IP头长度
if(x->props.mode==XFRM_MODE_TUNNEL)
x->props.header_len+=sizeof(structiphdr);
//SA数据指向AH数据结构
x->data=ahp;
return0;
error:
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()调用
staticvoidah_destroy(structxfrm_state*x)
{
structah_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);
}8.1.3.3协议输入//接收数据处理,在xfrm4_rcv_encap()函数中调用
//进行AH认证,剥离AH头
staticintah_input(structxfrm_state*x,structsk_buff*skb)
{
intah_hlen;
intihl;
interr=-EINVAL;
structiphdr*iph;
structip_auth_hdr*ah;
structah_data*ahp;
//IP头备份空间
charwork_buf[60];
//skb数据包要准备留出AH头空间
if(!
pskb_may_pull(skb,sizeof(structip_auth_hdr)))
gotoout;
//IP上层数据为AH数据
ah=(structip_auth_hdr*)skb->data;
//SA相关的AH处理数据
ahp=x->data;
ah_hlen=(ah->hdrlen+2)<<2;
//AH头部长度合法性检查
if(ah_hlen!
=XFRM_ALIGN8(sizeof(structip_auth_hdr)+ahp->icv_full_len)&&
ah_hlen!
=XFRM_ALIGN8(sizeof(structip_auth_hdr)+ahp->icv_trunc_len))
gotoout;
//skb数据包要准备留出实际AH头空间
if(!
pskb_may_pull(skb,ah_hlen))
gotoout;
/*Wearegoingto_remove_AHheadertokeepsocketshappy,
*so...Laterthiscanchange.*/
//对于clone的包要复制成独立包
if(skb_cloned(skb)&&
pskb_expand_head(skb,0,0,GFP_ATOMIC))
gotoout;
skb->ip_summed=CHECKSUM_NONE;
//可能包已经进行了复制,所以对ah重新赋值
ah=(structip_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)){
u32dummy;
if(ip_clear_mutable_options(iph,&dummy))
gotoout;
}
{
//认证数据缓冲区
u8auth_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)
gotoout;
err=-EINVAL;
//复制一定长度的认证数据作为初始化向量
if(memcmp(ahp->work_icv,auth_data,ahp->icv_trunc_len)){
x->stats.integrity_failed++;
gotoout;
}
}
//将备份的IP头缓冲区中的协议改为AH内部包裹的协议
((structiphdr*)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);
return0;
out:
returnerr;
}8.1.3.4协议输出//发送数据处理,在xfrm4_output_one()中调用
//计算AH认证值,添加AH头
staticintah_output(structxfrm_state*x,structsk_buff*skb)
{
interr;
structiphdr*iph,*top_iph;
structip_auth_hdr*ah;
structah_data*ahp;
//临时IP头缓冲区,最大IP头60字节
union{
structiphdriph;
charbuf[60];
}tmp_iph;
//当前的IP头将作为最外部IP头
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(structiphdr));
err=ip_clear_mutable_options(top_iph,&top_iph->daddr);
if(err)
gotoerror;
}
//AH头定位在外部IP头后面,skb缓冲中已经预留出AH头的数据部分了,
//这是通过mode->output函数预留的,通常调用type->output前要调用mode->oputput
ah=(structip_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(structip_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)
gotoerror;
//赋值初始化向量值到认证数据部分
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(structiphdr));
}
//重新计算IP头的认证值
ip_send_check(top_iph);
err=0;
error:
returnerr;
}
8.2ESP8.2.1初始化/*net/ipv4/esp4.c*/
staticint__initesp4_init(void)
{
//登记ESP协议的xfrm协议处理结构
if(xfrm_register_type(&esp_type,AF_INET)<0){
printk(KERN_INFO"ipespinit:
can'taddxfrmtype\n");
return-EAGAIN;
}
//登记ESP协议到IP协议
if(inet_add_protocol(&esp4_protocol,IPPROTO_ESP)<0){
printk(KERN_INFO"ipespinit:
can'taddprotocol\n");
xfrm_unregister_type(&esp_type,AF_INET);
return-EAGAIN;
}
return0;
}8.2.2IPV4下的ESP协议处理结构//ESP协议处理结构,接收到IPV4包后,系统根据IP头中的protocol
//字段选择相应的上层协议处理函数,当IP协议号是50时,数据包将
//调用该结构的handler处理函数:
st
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 内核 中的 ipsec 实现 转载