基於openswan klips的IPsec實現分析(十)認證算法維護
轉載請註明出處:http://blog.csdn.net/rosetta
這裏指的認證算法是ESP使用的,對通信過程中的信息做哈希,用來校驗信息的完整性的;協商時的認證算法僅用來做哈希,哈希結果再用來做簽名和驗籤。
相對於加密算法,認證算法會比較簡單些,它也不需要像加密算法一樣去構造一個結構體(當然如果需要也是可以這麼做的)。
發送方處理比較簡單,主要是由ipsec_xmit_encap_once()對加密後的部分數據做哈希,然後一起發給對方;接收方稍微複雜點,先把發送方已經計算好的那份哈希弄出來,再對密文用同樣的哈希算法做哈希,最後比較兩個結果。
詳細過程如下分析。
發送方:
發送方就一個函數ipsec_xmit_encap_once()。
ipsec_xmit_encap_once()函數
enum ipsec_xmit_value
ipsec_xmit_encap_once(structipsec_xmit_state *ixs)
{
printk("infunc:%s\n", "ipsec_xmit_encap_once");
#ifdef CONFIG_KLIPS_ESP
structesphdr *espp;
unsignedchar *idat, *pad;
intauthlen = 0, padlen = 0, i;
#endif /* !CONFIG_KLIPS_ESP */
#ifdef CONFIG_KLIPS_AH
structiphdr ipo;
structahhdr *ahp;
#endif /* CONFIG_KLIPS_AH */
#if defined(CONFIG_KLIPS_AUTH_HMAC_MD5) ||defined(CONFIG_KLIPS_AUTH_HMAC_SHA1)
union{
#ifdef CONFIG_KLIPS_AUTH_HMAC_MD5
MD5_CTXmd5;
#endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */
#ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1
SHA1_CTXsha1;
#endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */
}tctx;
__u8hash[AH_AMAX];
#endif /*defined(CONFIG_KLIPS_AUTH_HMAC_MD5) || defined(CONFIG_KLIPS_AUTH_HMACn_SHA1) */
intheadroom = 0, tailroom = 0, ilen = 0, len = 0;
unsignedchar *dat;
intblocksize = 8; /* XXX: should be inside ixs --jjo */
structipsec_alg_enc *ixt_e = NULL;
structipsec_alg_auth *ixt_a = NULL;
ixs->iphlen= ixs->iph->ihl << 2;
ixs->pyldsz= ntohs(ixs->iph->tot_len) - ixs->iphlen;
ixs->sa_len= satot(&ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOT_BUF);
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:ipsec_xmit_encap_once:"
"calling output for <%s%s%s>,SA:%s\n",
IPS_XFORM_NAME(ixs->ipsp),
ixs->sa_len ? ixs->sa_txt : "(error)");
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"ixs->ipsp->ips_said.proto:%d\n",
ixs->ipsp->ips_said.proto);
//因爲此函數(ipsec_xmit_encap_once)的外面是一個while(ixs->ipsp)循環,所以會循環sp鏈表,
//最常見的情況是隧道模式(IPIP)+ESP,但這兩個的協議號都是放在這個proto中的。
//所以會進這個函數兩次。
switch(ixs->ipsp->ips_said.proto){
#ifdef CONFIG_KLIPS_AH
caseIPPROTO_AH:
headroom+= sizeof(struct ahhdr);
break;
#endif /* CONFIG_KLIPS_AH */
#ifdef CONFIG_KLIPS_ESP
caseIPPROTO_ESP://esp協議號50.假如只esp無ah,走這裏。
//如果還有AH的話,會進這個函數三次。
{
//printk("in func:%s,%s\n", __func__, "IPPROTO_ESP");
ixt_e=ixs->ipsp->ips_alg_enc;//包含加密算法函數指針等結構體。--esp時指定的加密算法。
if(ixt_e) {
blocksize= ixt_e->ixt_common.ixt_blocksize;
//比如sm1算法的ixt_blocksize爲16,ocs算法爲8。
headroom+= ESP_HEADER_LEN + ixt_e->ixt_common.ixt_support.ias_ivlen/8;
//ESP_HEADER_LEN爲SPI(4字節)+序列號(4字節)=8字節,最後一個是數據,一般是IV。
//可看pdf版本rfc2406第二節圖.
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"ias_ivlen:%d\n",
ixt_e->ixt_common.ixt_support.ias_ivlen
);
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"ias_id:%d,ias_name:%s\n",
ixt_e->ixt_common.ixt_support.ias_id,
ixt_e->ixt_common.ixt_support.ias_name
);
//以esp 3des-md5爲例.
//ias_ivlen爲64
//isa_id爲3(正是3des算法的id)//whack--status 可以查看
//000algorithm ESP encrypt: id=3, name=ESP_3DES, ivlen=64, keysizemin=192,keysizemax=192
//ias_name爲NULL,不知道爲什麼?可能沒加到那個名字宏裏。
}else {
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_BADALG;
}
ixt_a=ixs->ipsp->ips_alg_auth;
if(ixt_a) {
tailroom+= AHHMAC_HASHLEN;
}else
{
//printk("21111111.\n");//走這
switch(ixs->ipsp->ips_authalg){
#ifdef CONFIG_KLIPS_AUTH_HMAC_MD5
caseAH_MD5:
authlen= AHHMAC_HASHLEN;
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */
#ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1
caseAH_SHA:
authlen= AHHMAC_HASHLEN;
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */
caseAH_NONE:
break;
default:
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_BADALG;
}
}
//認證長度12,爲什麼MD5,SHA1,甚至是SM3的認證長度都爲12?
//這個12的依據是?
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"1,authlen:%d, tailroom:%d\n",
authlen,tailroom);
tailroom+= blocksize != 1 ?
((blocksize- ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 :
((4- ((ixs->pyldsz + 2) % 4)) % 4) + 2;
tailroom+= authlen;
//這個應該根據rfc2406進行填充?
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"2,authlen:%d, tailroom:%d\n",
authlen,tailroom);
}
break;
#endif /* CONFIG_KLIPS_ESP */
#ifdef CONFIG_KLIPS_IPIP
caseIPPROTO_IPIP://什麼時候用IPIP?隧道模式,這裏只是分配了下空間。
headroom+= sizeof(struct iphdr);
ixs->iphlen= sizeof(struct iphdr);
break;
#endif /* !CONFIG_KLIPS_IPIP */
#ifdef CONFIG_KLIPS_IPCOMP
caseIPPROTO_COMP://壓縮?
break;
#endif /* CONFIG_KLIPS_IPCOMP */
default:
ixs->stats->tx_errors++;
returnIPSEC_XMIT_BADPROTO;
}
KLIPS_PRINT(debug_tunnel& DB_TN_CROUT,
"klips_debug:ipsec_xmit_encap_once:"
"pushing %d bytes, putting %d, proto%d.\n",
headroom, tailroom,ixs->ipsp->ips_said.proto);
if(skb_headroom(ixs->skb)< headroom) {
printk(KERN_WARNING
"klips_error:ipsec_xmit_encap_once:"
"tried to skb_push headroom=%d, %davailable. This should never happen,please report.\n",
headroom, skb_headroom(ixs->skb));
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_PUSHPULLERR;
}
//skb頭部預留headroom字節空間。
dat= skb_push(ixs->skb, headroom);
ilen= ixs->skb->len - tailroom;
if(skb_tailroom(ixs->skb)< tailroom) {
printk(KERN_WARNING
"klips_error:ipsec_xmit_encap_once:"
"tried to skb_put %d, %davailable. This should never happen,please report.\n",
tailroom, skb_tailroom(ixs->skb));
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_PUSHPULLERR;
}
//skb尾部預留tailroom字節空間。
skb_put(ixs->skb,tailroom);
KLIPS_PRINT(debug_tunnel& DB_TN_CROUT,
"klips_debug:ipsec_xmit_encap_once:"
"head,tailroom: %d,%d beforexform.\n",
skb_headroom(ixs->skb),skb_tailroom(ixs->skb));
len= ixs->skb->len;
if(len> 0xfff0) {
printk(KERN_WARNING"klips_error:ipsec_xmit_encap_once: "
"tot_len (%d) > 65520. This should never happen, pleasereport.\n",
len);
ixs->stats->tx_errors++;
returnIPSEC_XMIT_BADLEN;
}
memmove((void*)dat, (void *)(dat + headroom), ixs->iphlen);
ixs->iph= (struct iphdr *)dat;
ixs->iph->tot_len= htons(ixs->skb->len);
//不懂?//雖然沒怎麼看明白,在IPIP模式下,即隧道模式,這裏的目的猜是在ESP包前面加一個原始IP頭。
//這裏僅僅是加了空間,具體的賦值在下面操作。
switch(ixs->ipsp->ips_said.proto){
#ifdef CONFIG_KLIPS_ESP
caseIPPROTO_ESP:
espp= (struct esphdr *)(dat + ixs->iphlen);//dat是之前IPIP時加的IP原始頭指針。
//此時再加一個IP長度,那麼這個位置espp就是放ESP頭的位置。
espp->esp_spi= ixs->ipsp->ips_said.spi;
espp->esp_rpl= htonl(++(ixs->ipsp->ips_replaywin_lastseq));
if(!ixt_e) {
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_BADALG;
}
idat= dat + ixs->iphlen + headroom;
ilen= len - (ixs->iphlen + headroom + authlen);
/*Self-describing padding */
pad= &dat[len - tailroom];
padlen= tailroom - 2 - authlen;
for(i = 0; i < padlen; i++) {
pad[i]= i + 1;
}
dat[len- authlen - 2] = padlen;
dat[len- authlen - 1] = ixs->iph->protocol;
ixs->iph->protocol= IPPROTO_ESP;
//以上這段操作沒怎麼看懂。對需要加密/認證的數據進行數據補齊。
#ifdef CONFIG_KLIPS_DEBUG
if(debug_tunnel& DB_TN_ENCAP) {
dmp("pre-encrypt", dat,len);//最終需要加密的數據在dat裏。
}
#endif
/*
* Do all operations here:
* copy IV->ESP, encrypt, update ips IV
*
*/
{
intret;
memcpy(espp->esp_iv,
ixs->ipsp->ips_iv,
ixs->ipsp->ips_iv_size);
ret=ipsec_alg_esp_encrypt(ixs->ipsp,//加密核心函數。
idat, ilen, espp->esp_iv,
IPSEC_ALG_ENCRYPT);
prng_bytes(&ipsec_prng,
(char *)ixs->ipsp->ips_iv,
ixs->ipsp->ips_iv_size);
}
if(ixt_a) {
ipsec_alg_sa_esp_hash(ixs->ipsp,//認證核心(做哈希)。//如果不走這裏,那麼走下面分支也是一樣的。
(caddr_t)espp,len - ixs->iphlen - authlen,
&(dat[len- authlen]), authlen);
}else
switch(ixs->ipsp->ips_authalg){
#ifdef CONFIG_KLIPS_AUTH_HMAC_MD5
caseAH_MD5:
dmp("espp",(char*)espp, len - ixs->iphlen - authlen);
tctx.md5= ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
dmp("ictx",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Update(&tctx.md5,(caddr_t)espp, len - ixs->iphlen - authlen);
dmp("ictx+dat",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Final(hash,&tctx.md5);
dmp("ictxhash", (char*)&hash, sizeof(hash));
tctx.md5= ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
dmp("octx",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Update(&tctx.md5,hash, AHMD596_ALEN);
dmp("octx+hash",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Final(hash,&tctx.md5);
dmp("octxhash", (char*)&hash, sizeof(hash));
memcpy(&(dat[len- authlen]), hash, authlen);
/*paranoid */
memset((caddr_t)&tctx.md5,0, sizeof(tctx.md5));
memset((caddr_t)hash,0, sizeof(*hash));
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */
#ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1
caseAH_SHA:
tctx.sha1= ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
SHA1Update(&tctx.sha1,(caddr_t)espp, len - ixs->iphlen - authlen);
SHA1Final(hash,&tctx.sha1);
tctx.sha1= ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
SHA1Update(&tctx.sha1,hash, AHSHA196_ALEN);
SHA1Final(hash,&tctx.sha1);
memcpy(&(dat[len- authlen]), hash, authlen);
/*paranoid */
memset((caddr_t)&tctx.sha1,0, sizeof(tctx.sha1));
memset((caddr_t)hash,0, sizeof(*hash));
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */
caseAH_NONE:
break;
default:
ixs->stats->tx_errors++;
returnIPSEC_XMIT_AH_BADALG;
}
#ifdef NET_21
ixs->skb->h.raw= (unsigned char*)espp;
#endif /* NET_21 */
break;
#endif /* !CONFIG_KLIPS_ESP */
#ifdef CONFIG_KLIPS_AH
caseIPPROTO_AH:
ahp= (struct ahhdr *)(dat + ixs->iphlen);
ahp->ah_spi= ixs->ipsp->ips_said.spi;
ahp->ah_rpl= htonl(++(ixs->ipsp->ips_replaywin_lastseq));
ahp->ah_rv= 0;
ahp->ah_nh= ixs->iph->protocol;
ahp->ah_hl= (headroom >> 2) - sizeof(__u64)/sizeof(__u32);
ixs->iph->protocol= IPPROTO_AH;
dmp("ahp",(char*)ahp, sizeof(*ahp));
ipo= *ixs->iph;
ipo.tos= 0;
ipo.frag_off= 0;
ipo.ttl= 0;
ipo.check= 0;
dmp("ipo",(char*)&ipo, sizeof(ipo));
switch(ixs->ipsp->ips_authalg){
#ifdef CONFIG_KLIPS_AUTH_HMAC_MD5
caseAH_MD5:
tctx.md5= ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
dmp("ictx",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Update(&tctx.md5,(unsigned char *)&ipo, sizeof (struct iphdr));
dmp("ictx+ipo",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Update(&tctx.md5,(unsigned char *)ahp, headroom - sizeof(ahp->ah_data));
dmp("ictx+ahp",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Update(&tctx.md5,(unsigned char *)zeroes, AHHMAC_HASHLEN);
dmp("ictx+zeroes",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Update(&tctx.md5, dat + ixs->iphlen + headroom, len -ixs->iphlen - headroom);
dmp("ictx+dat",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Final(hash,&tctx.md5);
dmp("ictxhash", (char*)&hash, sizeof(hash));
tctx.md5= ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
dmp("octx",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Update(&tctx.md5,hash, AHMD596_ALEN);
dmp("octx+hash",(char*)&tctx.md5, sizeof(tctx.md5));
osMD5Final(hash,&tctx.md5);
dmp("octxhash", (char*)&hash, sizeof(hash));
memcpy(ahp->ah_data,hash, AHHMAC_HASHLEN);
/*paranoid */
memset((caddr_t)&tctx.md5,0, sizeof(tctx.md5));
memset((caddr_t)hash,0, sizeof(*hash));
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */
#ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1
caseAH_SHA:
tctx.sha1= ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
SHA1Update(&tctx.sha1,(unsigned char *)&ipo, sizeof (struct iphdr));
SHA1Update(&tctx.sha1,(unsigned char *)ahp, headroom - sizeof(ahp->ah_data));
SHA1Update(&tctx.sha1,(unsigned char *)zeroes, AHHMAC_HASHLEN);
SHA1Update(&tctx.sha1, dat + ixs->iphlen + headroom, len -ixs->iphlen - headroom);
SHA1Final(hash,&tctx.sha1);
tctx.sha1= ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
SHA1Update(&tctx.sha1,hash, AHSHA196_ALEN);
SHA1Final(hash,&tctx.sha1);
memcpy(ahp->ah_data,hash, AHHMAC_HASHLEN);
/*paranoid */
memset((caddr_t)&tctx.sha1,0, sizeof(tctx.sha1));
memset((caddr_t)hash,0, sizeof(*hash));
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */
default:
ixs->stats->tx_errors++;
returnIPSEC_XMIT_AH_BADALG;
}
#ifdef NET_21
ixs->skb->h.raw= (unsigned char*)ahp;
#endif /* NET_21 */
break;
#endif /* CONFIG_KLIPS_AH */
#ifdef CONFIG_KLIPS_IPIP
caseIPPROTO_IPIP:
ixs->iph->version = 4;
switch(sysctl_ipsec_tos){
case0:
#ifdef NET_21
ixs->iph->tos= ixs->skb->nh.iph->tos;
#else /* NET_21 */
ixs->iph->tos= ixs->skb->ip_hdr->tos;
#endif /* NET_21 */
break;
case1:
ixs->iph->tos= 0;
break;
default:
break;
}
ixs->iph->ttl = SYSCTL_IPSEC_DEFAULT_TTL;
ixs->iph->frag_off= 0;
ixs->iph->saddr = ((structsockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr;
ixs->iph->daddr = ((structsockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr;
ixs->iph->protocol= IPPROTO_IPIP;
ixs->iph->ihl = sizeof(struct iphdr) >> 2;
printk("IPIPsrc addr:%x\n", ntohl((unsigned int)ixs->iph->saddr));
printk("IPIPdst addr:%x\n", ntohl((unsigned int)ixs->iph->daddr));
//PIPsrc addr:c0a85fa2//即192.168.95.162
//IPIPdst addr:c0a85fe6//192.168.95.230
//所以隧道模式網絡層封包的最外層是外網口IP(即協商口IP)。
KLIPS_IP_SELECT_IDENT(ixs->iph,ixs->skb);
ixs->newdst= (__u32)ixs->iph->daddr;
ixs->newsrc= (__u32)ixs->iph->saddr;
#ifdef NET_21
ixs->skb->h.ipiph= ixs->skb->nh.iph;
#endif /* NET_21 */
break;
#endif /* !CONFIG_KLIPS_IPIP */
#ifdef CONFIG_KLIPS_IPCOMP
caseIPPROTO_COMP:
{
unsignedint flags = 0;
#ifdef CONFIG_KLIPS_DEBUG
unsignedint old_tot_len = ntohs(ixs->iph->tot_len);
#endif /* CONFIG_KLIPS_DEBUG */
ixs->ipsp->ips_comp_ratio_dbytes+= ntohs(ixs->iph->tot_len);
ixs->skb= skb_compress(ixs->skb, ixs->ipsp, &flags);
#ifdef NET_21
ixs->iph= ixs->skb->nh.iph;
#else /* NET_21 */
ixs->iph= ixs->skb->ip_hdr;
#endif /* NET_21 */
ixs->ipsp->ips_comp_ratio_cbytes+= ntohs(ixs->iph->tot_len);
#ifdef CONFIG_KLIPS_DEBUG
if(debug_tunnel & DB_TN_CROUT)
{
if(old_tot_len > ntohs(ixs->iph->tot_len))
KLIPS_PRINT(debug_tunnel& DB_TN_CROUT,
"klips_debug:ipsec_xmit_encap_once:"
"packet shrunk from %d to %d bytesafter compression, cpi=%04x (should be from spi=%08x,spi&0xffff=%04x.\n",
old_tot_len,ntohs(ixs->iph->tot_len),
ntohs(((structipcomphdr*)(((char*)ixs->iph) + ((ixs->iph->ihl) <<2)))->ipcomp_cpi),
ntohl(ixs->ipsp->ips_said.spi),
(__u16)(ntohl(ixs->ipsp->ips_said.spi)& 0x0000ffff));
else
KLIPS_PRINT(debug_tunnel& DB_TN_CROUT,
"klips_debug:ipsec_xmit_encap_once:"
"packet did not compress (flags =%d).\n",
flags);
}
#endif /* CONFIG_KLIPS_DEBUG */
}
break;
#endif /* CONFIG_KLIPS_IPCOMP */
default:
ixs->stats->tx_errors++;
returnIPSEC_XMIT_BADPROTO;
}
#ifdef NET_21
ixs->skb->nh.raw= ixs->skb->data;
#else /* NET_21 */
ixs->skb->ip_hdr= ixs->skb->h.iph = (struct iphdr *) ixs->skb->data;
#endif /* NET_21 */
ixs->iph->check= 0;
ixs->iph->check= ip_fast_csum((unsigned char *)ixs->iph, ixs->iph->ihl);
KLIPS_PRINT(debug_tunnel& DB_TN_XMIT,
"klips_debug:ipsec_xmit_encap_once:"
"after <%s%s%s>, SA:%s:\n",
IPS_XFORM_NAME(ixs->ipsp),
ixs->sa_len ? ixs->sa_txt : "(error)");
KLIPS_IP_PRINT(debug_tunnel& DB_TN_XMIT, ixs->iph);
ixs->ipsp->ips_life.ipl_bytes.ipl_count+= len;
ixs->ipsp->ips_life.ipl_bytes.ipl_last= len;
if(!ixs->ipsp->ips_life.ipl_usetime.ipl_count){
ixs->ipsp->ips_life.ipl_usetime.ipl_count= jiffies / HZ;
}
ixs->ipsp->ips_life.ipl_usetime.ipl_last= jiffies / HZ;
ixs->ipsp->ips_life.ipl_packets.ipl_count++;
ixs->ipsp= ixs->ipsp->ips_onext;
returnIPSEC_XMIT_OK;
}
接收方:
在“數據接收”一節已經介紹過數據的接收過程,Ipsec_rcv()從物理網卡獲取到數據包,走到ipsec_rcv_decap()。
在ipsec_rcv_decap() 裏依據ESP、AH或COMP選擇對應的xform_functions結構體,此結構包含認證函數指針、解密函數指針。再進入ipsec_rcv_decap_once(),proto_funcs->rcv_checks檢查ESP包是否是4字節對齊(rfc24062.4節),接着判斷是否存在有效的SA;proto_funcs->rcv_setup_auth設置哈希檢驗函數相關結構體;由proto_funcs->rcv_calc_auth對密文做哈希,比較此哈希值和發送方發過來的哈希值是否一樣,如果一樣表明數據在傳送過程中沒有發生異常,最後由proto_funcs->rcv_decrypt解密,解完後將數據扔給上層協議棧處理。
以下只給出認證相關的數據結構和函數代碼片段。
相關結構體:
struct ipsec_rcv_state;
struct ipsec_xmit_state;
struct xform_functions {
enum ipsec_rcv_value (*rcv_checks)(struct ipsec_rcv_state *irs,
struct sk_buff *skb);
enum ipsec_rcv_value (*rcv_decrypt)(struct ipsec_rcv_state *irs);
enum ipsec_rcv_value (*rcv_setup_auth)(struct ipsec_rcv_state *irs,
struct sk_buff *skb,
__u32 *replay,
unsigned char **authenticator);
enum ipsec_rcv_value (*rcv_calc_auth)(struct ipsec_rcv_state *irs,
struct sk_buff *skb);
enum ipsec_xmit_value (*xmit_setup)(struct ipsec_xmit_state *ixs);
enum ipsec_xmit_value (*xmit_encrypt)(struct ipsec_xmit_state *ixs);
enum ipsec_xmit_value (*xmit_setup_auth)(struct ipsec_xmit_state *ixs,
struct sk_buff *skb,
__u32 *replay,
unsigned char **authenticator);
enum ipsec_xmit_value (*xmit_calc_auth)(struct ipsec_xmit_state *ixs,
struct sk_buff *skb);
int xmit_headroom;
int xmit_needtailroom;
};
struct xform_functions esp_xform_funcs[]={
{ rcv_checks: ipsec_rcv_esp_checks,//協議類型,長度檢查;spi賦值等。
rcv_setup_auth: ipsec_rcv_esp_decrypt_setup,//定位ESP頭,獲取發送方已經計算好的哈希值。
rcv_calc_auth: ipsec_rcv_esp_authcalc,//對發送方的信息做哈希。
rcv_decrypt: ipsec_rcv_esp_decrypt,//解密。
xmit_setup: ipsec_xmit_esp_setup,
xmit_headroom: sizeof(structesphdr),
xmit_needtailroom: 1,
},
};
ipsec_rcv_decap()函數
/*
*core decapsulation loop for all protocols.
*
*the following things should be setup to enter this function.
*
*irs->stats == stats structure (orNULL)
*irs->ipp = IP header.
*irs->ipsp = NULL.
*irs->ilen = 0;
*irs->authlen = 0;
*irs->authfuncs = NULL;
*irs->skb = skb;
*skb->nh.iph = ipp;
*skb->h.raw = start of payload
*
*/
int ipsec_rcv_decap(structipsec_rcv_state *irs)
{
struct ipsec_sa *ipsp = NULL;
structipsec_sa* ipsnext = NULL;
structin_addr ipsaddr;
structin_addr ipdaddr;
structiphdr *ipp;
structsk_buff *skb = NULL;
/*begin decapsulating loop here */
/*
The spinlock is to prevent any other processfrom
accessing or deleting the ipsec_sa hash tableor any of the
ipsec_sa s while we are using and updatingthem.
This is not optimal, but was relativelystraightforward
at the time. A better way to do it has been planned for
more than a year, to lock the hash table andput reference
counts on each ipsec_sa instead. This is not likely to happen
in KLIPS1 unless a volunteer contributes it,but will be
designed into KLIPS2.
*/
spin_lock(&tdb_lock);//加鎖
do{//給proto_funcs賦值
int decap_stat;
structxform_functions *proto_funcs;
switch(irs->ipp->protocol){
caseIPPROTO_ESP:
proto_funcs=esp_xform_funcs;//proto_funcs賦值爲esp_xform_funcs
break;
#ifdef CONFIG_KLIPS_AH
caseIPPROTO_AH:
proto_funcs = ah_xform_funcs;
break;
#endif /* !CONFIG_KLIPS_AH */
#ifdef CONFIG_KLIPS_IPCOMP
caseIPPROTO_COMP:
proto_funcs = ipcomp_xform_funcs;
break;
#endif /* !CONFIG_KLIPS_IPCOMP */
default:
if(irs->stats) {
irs->stats->rx_errors++;
}
decap_stat = IPSEC_RCV_BADPROTO;
goto rcvleave;
}
decap_stat = ipsec_rcv_decap_once(irs,proto_funcs);
if(decap_stat!= IPSEC_RCV_OK) {
spin_unlock(&tdb_lock);
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: decap_oncefailed: %d\n",
decap_stat);
gotorcvleave;
}
/*end decapsulation loop here */
}
//以下部分代碼省略
//…..
#ifdefSKB_RESET_NFCT
nf_conntrack_put(skb->nfct);
skb->nfct = NULL;
#ifdefined(CONFIG_NETFILTER_DEBUG) && defined(HAVE_SKB_NF_DEBUG)
skb->nf_debug = 0;
#endif /*CONFIG_NETFILTER_DEBUG */
#endif /*SKB_RESET_NFCT */
KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
"klips_debug:ipsec_rcv: "
"netif_rx() called.\n");
netif_rx(skb);//將數據扔給上層協議棧處理。
skb=NULL;
rcvleave:
if(skb) {
ipsec_kfree_skb(skb);
}
/* KLIPS_DEC_USE; Artifact from refactor?bug # 454 */
return(0);
}
ipsec_rcv_decap_once ()函數
/*
*decapsulate a single layer of the system
*
*the following things should be setup to enter this function.
*
*irs->stats == stats structure (orNULL)
*irs->ipp = IP header.
*irs->len = total length of packet
*skb->nh.iph = ipp;
*skb->h.raw = start of payload
*irs->ipsp = NULL.
*irs->iphlen = N/A = is recalculated.
*irs->ilen = 0;
*irs->authlen = 0;
*irs->authfuncs = NULL;
*irs->skb = the skb;
*
*proto_funcs should be from ipsec_esp.c, ipsec_ah.c or ipsec_ipcomp.c.
*
*/
enum ipsec_rcv_value
ipsec_rcv_decap_once(struct ipsec_rcv_state*irs
, struct xform_functions *proto_funcs)
{
intiphlen;
__u8proto;
structin_addr ipsaddr;
structin_addr ipdaddr;
intreplay = 0; /* replay value in AH or ESPpacket */
structipsec_sa* ipsnext = NULL; /* next SA towardsinside of packet */
structipsec_sa *newipsp;
structiphdr *ipp;
structsk_buff *skb;
structipsec_alg_auth *ixt_a=NULL;
skb= irs->skb;
irs->len= skb->len;
ipp= irs->ipp;
proto= ipp->protocol;
ipsaddr.s_addr= ipp->saddr;
addrtoa(ipsaddr,0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt));
ipdaddr.s_addr= ipp->daddr;
addrtoa(ipdaddr,0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt));
iphlen= ipp->ihl << 2;
irs->iphlen=iphlen;
ipp->check= 0; /* we knowthe sum is good */
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv_decap_once:"
"decap (%d) from %s -> %s\n",
proto, irs->ipsaddr_txt,irs->ipdaddr_txt);
/*
* Find tunnel control block and (indirectly)call the
* appropriate tranform routine. The resultingsk_buf
* is a valid IP packet ready to go throughinput processing.
*/
irs->said.dst.u.v4.sin_addr.s_addr= ipp->daddr;
irs->said.dst.u.v4.sin_family= AF_INET;
/*note: rcv_checks set up the said.spi value, if appropriate */
if(proto_funcs->rcv_checks){// ipsec_rcv_esp_checks,做相關檢查。
enumipsec_rcv_value retval =
(*proto_funcs->rcv_checks)(irs, skb);
if(retval< 0) {
returnretval;
}
}
irs->said.proto= proto;
irs->sa_len= satot(&irs->said, 0, irs->sa, sizeof(irs->sa));
if(irs->sa_len== 0) {
strcpy(irs->sa,"(error)");
}
newipsp= ipsec_sa_getbyid(&irs->said);//獲得sa
if(newipsp == NULL) {
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"no ipsec_sa for SA:%s: incomingpacket with no SA dropped\n",
irs->sa_len ? irs->sa : "(error)");
if(irs->stats){
irs->stats->rx_dropped++;
}
returnIPSEC_RCV_SAIDNOTFOUND;
}
/*If it is in larval state, drop the packet, we cannot process yet. */
if(newipsp->ips_state== SADB_SASTATE_LARVAL) {
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"ipsec_sa in larval state, cannot beused yet, dropping packet.\n");
if(irs->stats){
irs->stats->rx_dropped++;
}
ipsec_sa_put(newipsp);
returnIPSEC_RCV_SAIDNOTLIVE;
}
if(newipsp->ips_state== SADB_SASTATE_DEAD) {
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"ipsec_sa in dead state, cannot beused any more, dropping packet.\n");
if(irs->stats){
irs->stats->rx_dropped++;
}
ipsec_sa_put(newipsp);
returnIPSEC_RCV_SAIDNOTLIVE;
}
if(sysctl_ipsec_inbound_policy_check){
if(irs->ipp->saddr!= ((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr) {
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"SA:%s, src=%s of pkt does not agreewith expected SA source address policy.\n",
irs->sa_len ? irs->sa : " (error)",
irs->ipsaddr_txt);
if(irs->stats){
irs->stats->rx_dropped++;
}
ipsec_sa_put(newipsp);
returnIPSEC_RCV_FAILEDINBOUND;
}
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"SA:%s, src=%s of pkt agrees withexpected SA source address policy.\n",
irs->sa_len ? irs->sa : "(error)",
irs->ipsaddr_txt);
/*
* at this point, we have looked up a new SA,and we want to make sure that if this
* isn't the first SA in the list, that theprevious SA actually points at this one.
*/
if(irs->ipsp){
if(irs->ipsp->ips_inext!= newipsp) {
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"unexpected SA:%s: does not agree withips->inext policy, dropped\n",
irs->sa_len ? irs->sa : "(error)");
if(irs->stats){
irs->stats->rx_dropped++;
}
ipsec_sa_put(newipsp);
returnIPSEC_RCV_FAILEDINBOUND;
}
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"SA:%s grouping from previous SA isOK.\n",
irs->sa_len ? irs->sa : "(error)");
}else {
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"SA:%s First SA in group.\n",
irs->sa_len ? irs->sa : "(error)");
}
}
/*okay, SA checks out, so free any previous SA, and record a new one*/
if(irs->ipsp){
ipsec_sa_put(irs->ipsp);
}
irs->ipsp=newipsp;
/*note that the outer code will free the irs->ipsp
if there is an error */
/*now check the lifetimes */
if(ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_bytes, "bytes",
irs->sa,ipsec_life_countbased, ipsec_incoming,
irs->ipsp) ==ipsec_life_harddied ||
ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime,"addtime",
irs->sa,ipsec_life_timebased, ipsec_incoming,
irs->ipsp)== ipsec_life_harddied ||
ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime,"usetime",
irs->sa,ipsec_life_timebased, ipsec_incoming,
irs->ipsp)== ipsec_life_harddied ||
ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_packets,"packets",
irs->sa,ipsec_life_countbased, ipsec_incoming,
irs->ipsp)== ipsec_life_harddied) {
ipsec_sa_delchain(irs->ipsp);
if(irs->stats){
irs->stats->rx_dropped++;
}
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv_decap_once:"
"decap (%d) failed lifetimecheck\n",
proto);
returnIPSEC_RCV_LIFETIMEFAILED;
}
irs->authfuncs=NULL;
/*authenticate, if required */
if((ixt_a=irs->ipsp->ips_alg_auth)) {//這個ips_alg_auth類型是認證算法結構體指針structipsec_alg_auth,這個和加密算法結構體類似structipsec_alg_enc,但是做認證的時候一般不會在這個指針上賦值,而是直接走下面分支中的case。
irs->authlen= AHHMAC_HASHLEN;
irs->authfuncs= NULL;
irs->ictx= NULL;
irs->octx= NULL;
irs->ictx_len= 0;
irs->octx_len= 0;
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv:"
"authalg=%dauthlen=%d\n",
irs->ipsp->ips_authalg,
irs->authlen);
}else
switch(irs->ipsp->ips_authalg){//走這。
#ifdef CONFIG_KLIPS_AUTH_HMAC_MD5
caseAH_MD5:
irs->authlen= AHHMAC_HASHLEN;//認證長度12Byte,96bits
irs->authfuncs= ipsec_rcv_md5;//接收方HASH計算函數
irs->ictx= (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx;
irs->octx= (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx;
irs->ictx_len= sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx);
irs->octx_len= sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx);
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */
#ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1
caseAH_SHA:
irs->authlen= AHHMAC_HASHLEN;
irs->authfuncs= ipsec_rcv_sha1;
irs->ictx= (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx;
irs->octx= (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx;
irs->ictx_len= sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx);
irs->octx_len= sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx);
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */
caseAH_NONE:
irs->authlen= 0;
irs->authfuncs= NULL;
irs->ictx= NULL;
irs->octx= NULL;
irs->ictx_len= 0;
irs->octx_len= 0;
break;
default:
irs->ipsp->ips_errs.ips_alg_errs+= 1;
if(irs->stats){
irs->stats->rx_errors++;
}
returnIPSEC_RCV_BADAUTH;
}
/*ilen counts number of bytes in ESP portion */
irs->ilen= ((skb->data + skb->len) - skb->h.raw) - irs->authlen;
if(irs->ilen<= 0) {
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"runt %s packet with no data,dropping.\n",
(proto == IPPROTO_ESP ? "esp" :"ah"));
if(irs->stats) {
irs->stats->rx_dropped++;
}
return IPSEC_RCV_BADLEN;
}
if(irs->authfuncs|| ixt_a) {
unsignedchar *authenticator = NULL;
if(proto_funcs->rcv_setup_auth){
enumipsec_rcv_value retval
= (*proto_funcs->rcv_setup_auth)(irs,skb,//ipsec_rcv_esp_decrypt_setup,獲取發送方已經計算好的哈希值。
&replay,
&authenticator);
if(retval< 0) {
returnretval;
}
}
if(!authenticator){
irs->ipsp->ips_errs.ips_auth_errs+= 1;
if(irs->stats){
irs->stats->rx_dropped++;
}
returnIPSEC_RCV_BADAUTH;
}
if(!ipsec_checkreplaywindow(irs->ipsp,replay)) {
irs->ipsp->ips_errs.ips_replaywin_errs+= 1;
KLIPS_PRINT(debug_rcv &DB_RX_REPLAY,
"klips_debug:ipsec_rcv: "
"duplicate frame from %s, packetdropped\n",
irs->ipsaddr_txt);
if(irs->stats){
irs->stats->rx_dropped++;
}
returnIPSEC_RCV_REPLAYFAILED;
}
/*
* verify authenticator
*/
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"encalg = %d, authalg = %d.\n",
irs->ipsp->ips_encalg,
irs->ipsp->ips_authalg);
/*calculate authenticator */
if(proto_funcs->rcv_calc_auth== NULL) {
returnIPSEC_RCV_BADAUTH;
}
(*proto_funcs->rcv_calc_auth)(irs,skb);// ipsec_rcv_esp_authcalc()對發送方的信息做哈希.
if(memcmp(irs->hash, authenticator, irs->authlen)) {//比較兩個哈希值是否相同。
irs->ipsp->ips_errs.ips_auth_errs+= 1;
KLIPS_PRINT(debug_rcv& DB_RX_INAU,
"klips_debug:ipsec_rcv: "
"auth failed on incoming packet from%s: hash=%08x%08x%08x auth=%08x%08x%08x, dropped\n",
irs->ipsaddr_txt,
ntohl(*(__u32*)&irs->hash[0]),
ntohl(*(__u32*)&irs->hash[4]),
ntohl(*(__u32*)&irs->hash[8]),
ntohl(*(__u32*)authenticator),
ntohl(*((__u32*)authenticator + 1)),
ntohl(*((__u32*)authenticator + 2)));
if(irs->stats){
irs->stats->rx_dropped++;
}
returnIPSEC_RCV_AUTHFAILED;
}else {
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"authenticationsuccessful.\n");//認證成功。
}
/*Crypto hygiene: clear memory used to calculate autheticator.
* The length varies with the algorithm.
*/
memset(irs->hash,0, irs->authlen);
/*If the sequence number == 0, expire SA, it had rolled */
if(irs->ipsp->ips_replaywin&& !replay /* !irs->ipsp->ips_replaywin_lastseq */) {
ipsec_sa_delchain(irs->ipsp);
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"replay window counter rolled, expiringSA.\n");
if(irs->stats){
irs->stats->rx_dropped++;
}
returnIPSEC_RCV_REPLAYROLLED;
}
/*now update the replay counter */
if(!ipsec_updatereplaywindow(irs->ipsp, replay)) {
irs->ipsp->ips_errs.ips_replaywin_errs+= 1;
KLIPS_PRINT(debug_rcv& DB_RX_REPLAY,
"klips_debug:ipsec_rcv: "
"duplicate frame from %s, packetdropped\n",
irs->ipsaddr_txt);
if(irs->stats){
irs->stats->rx_dropped++;
}
returnIPSEC_RCV_REPLAYROLLED;
}
}
if(proto_funcs->rcv_decrypt){// ipsec_rcv_esp_decrypt()解密。
enumipsec_rcv_value retval =
(*proto_funcs->rcv_decrypt)(irs);
if(retval!= IPSEC_RCV_OK) {
returnretval;
}
}
/*
* Adjustpointers
*/
skb= irs->skb;
irs->len= skb->len;
ipp= irs->ipp = skb->nh.iph;
iphlen= ipp->ihl<<2;
skb->h.raw= skb->nh.raw + iphlen;
/*zero any options that there might be */
memset(&(IPCB(skb)->opt),0, sizeof(struct ip_options));
ipsaddr.s_addr= ipp->saddr;
addrtoa(ipsaddr,0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt));
ipdaddr.s_addr= ipp->daddr;
addrtoa(ipdaddr,0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt));
/*
* Discardthe original ESP/AH header
*/
ipp->protocol= irs->next_header;
ipp->check= 0; /* NOTE: this will beincluded in checksum */
ipp->check= ip_fast_csum((unsigned char *)skb->nh.iph, iphlen >> 2);
KLIPS_PRINT(debug_rcv& DB_RX_PKTRX,
"klips_debug:ipsec_rcv: "
"after <%s%s%s>, SA:%s:\n",
IPS_XFORM_NAME(irs->ipsp),
irs->sa_len ? irs->sa : "(error)");
KLIPS_IP_PRINT(debug_rcv& DB_RX_PKTRX, ipp);
skb->protocol= htons(ETH_P_IP);
skb->ip_summed= 0;
ipsnext= irs->ipsp->ips_inext;
if(sysctl_ipsec_inbound_policy_check){
if(ipsnext){
if(
ipp->protocol!= IPPROTO_AH
&&ipp->protocol != IPPROTO_ESP
#ifdef CONFIG_KLIPS_IPCOMP
&&ipp->protocol != IPPROTO_COMP
&&(ipsnext->ips_said.proto != IPPROTO_COMP
|| ipsnext->ips_inext)
#endif /* CONFIG_KLIPS_IPCOMP */
&&ipp->protocol != IPPROTO_IPIP
&&ipp->protocol != IPPROTO_ATT_HEARTBEAT /* heartbeats to AT&T SIG/GIG */
){
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"packet with incomplete policydropped, last successful SA:%s.\n",
irs->sa_len ? irs->sa : "(error)");
if(irs->stats){
irs->stats->rx_dropped++;
}
returnIPSEC_RCV_FAILEDINBOUND;
}
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"SA:%s, Another IPSEC header to process.\n",
irs->sa_len ? irs->sa : "(error)");
}else {
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"No ips_inext from thisSA:%s.\n",
irs->sa_len ? irs->sa : "(error)");
}
}
#ifdef CONFIG_KLIPS_IPCOMP
/*update ipcomp ratio counters, even if no ipcomp packet is present */
if(ipsnext
&& ipsnext->ips_said.proto ==IPPROTO_COMP
&& ipp->protocol !=IPPROTO_COMP) {
ipsnext->ips_comp_ratio_cbytes+= ntohs(ipp->tot_len);
ipsnext->ips_comp_ratio_dbytes+= ntohs(ipp->tot_len);
}
#endif /* CONFIG_KLIPS_IPCOMP */
irs->ipsp->ips_life.ipl_bytes.ipl_count+= irs->len;
irs->ipsp->ips_life.ipl_bytes.ipl_last = irs->len;
if(!irs->ipsp->ips_life.ipl_usetime.ipl_count){
irs->ipsp->ips_life.ipl_usetime.ipl_count= jiffies / HZ;
}
irs->ipsp->ips_life.ipl_usetime.ipl_last= jiffies / HZ;
irs->ipsp->ips_life.ipl_packets.ipl_count+= 1;
#ifdef CONFIG_NETFILTER
if(proto== IPPROTO_ESP || proto == IPPROTO_AH) {
skb->nfmark= (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_MASK))))
|IPsecSAref2NFmark(IPsecSA2SAref(irs->ipsp));
KLIPS_PRINT(debug_rcv& DB_RX_PKTRX,
"klips_debug:ipsec_rcv: "
"%s SA setsskb->nfmark=0x%x.\n",
proto == IPPROTO_ESP ? "ESP" :"AH",
(unsigned)skb->nfmark);
}
#endif /* CONFIG_NETFILTER */
returnIPSEC_RCV_OK;
}
ipsec_rcv_esp_checks()函數
enum ipsec_rcv_value
ipsec_rcv_esp_checks(struct ipsec_rcv_state*irs,
struct sk_buff *skb)
{
__u8proto;
intlen; /* packet length */
len= skb->len;
proto= irs->ipp->protocol;
/*XXX this will need to be 8 for IPv6 */
if((proto == IPPROTO_ESP) && ((len - irs->iphlen) % 4)) {
printk("klips_error:ipsec_rcv:"
"got packet with content length =%d from %s -- should be on 4 octet boundary, packet dropped\n",
len - irs->iphlen,
irs->ipsaddr_txt);
if(irs->stats){
irs->stats->rx_errors++;
}
returnIPSEC_RCV_BADLEN;
}
if(skb->len< (irs->hard_header_len + sizeof(struct iphdr) + sizeof(struct esphdr))){
KLIPS_PRINT(debug_rcv& DB_RX_INAU,
"klips_debug:ipsec_rcv: "
"runt esp packet of skb->len=%dreceived from %s, dropped.\n",
skb->len,
irs->ipsaddr_txt);
if(irs->stats){
irs->stats->rx_errors++;
}
returnIPSEC_RCV_BADLEN;
}
irs->protostuff.espstuff.espp= (struct esphdr *)skb->h.raw;
irs->said.spi= irs->protostuff.espstuff.espp->esp_spi;
returnIPSEC_RCV_OK;
}
ipsec_rcv_esp_decrypt_setup()函數
enum ipsec_rcv_value
ipsec_rcv_esp_decrypt_setup(structipsec_rcv_state *irs,
struct sk_buff *skb,
__u32 *replay,
unsigned char **authenticator)
{
structesphdr *espp = irs->protostuff.espstuff.espp;
//unsignedchar *idat = (unsigned char *)espp;
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"packet from %s received with seq=%d(iv)=0x%08x%08x iplen=%d esplen=%d sa=%s\n",
irs->ipsaddr_txt,
(__u32)ntohl(espp->esp_rpl),
(__u32)ntohl(*((__u32*)(espp->esp_iv) )),
(__u32)ntohl(*((__u32 *)(espp->esp_iv) +1)),
irs->len,
irs->ilen,
irs->sa_len ? irs->sa : "(error)");
*replay= ntohl(espp->esp_rpl);
*authenticator= &(skb->h.raw[irs->ilen]); //發送方計算出來的哈希值。
returnIPSEC_RCV_OK;
}
ipsec_rcv_esp_decrypt()函數
enum ipsec_rcv_value
ipsec_rcv_esp_decrypt(structipsec_rcv_state *irs)
{
//printk("infunc:%s\n", __func__);
structipsec_sa *ipsp = irs->ipsp;
structesphdr *espp = irs->protostuff.espstuff.espp;
inti;
intpad = 0, padlen;
intbadpad = 0;
intesphlen = 0;
__u8*idat; /* pointer to content to bedecrypted/authenticated */
intencaplen = 0;
structsk_buff *skb;
structipsec_alg_enc *ixt_e=NULL;
skb=irs->skb;
idat= skb->h.raw;
/*encaplen is the distance between the end of the IP
* header and the beginning of the ESP header.
* on ESP headers it is zero, but on UDP-encapESP
* it includes the space for the UDP header.
*
* Note: UDP-encap code has already moved the
* skb->data forward to accomodate this.
*/
encaplen= idat - (skb->nh.raw + irs->iphlen);
ixt_e=ipsp->ips_alg_enc;
esphlen= ESP_HEADER_LEN + ixt_e->ixt_common.ixt_support.ias_ivlen/8;
KLIPS_PRINT(debug_rcv,
"klips_debug:ipsec_rcv: "
"encalg=%d esphlen=%d\n",
ipsp->ips_encalg, esphlen);
idat+= esphlen;
irs->ilen-= esphlen;
if(ipsec_alg_esp_encrypt(ipsp,
idat, irs->ilen, espp->esp_iv,
IPSEC_ALG_DECRYPT) <= 0) {
#ifdef CONFIG_KLIPS_DEBUG
KLIPS_ERROR(debug_rcv,"klips_error:ipsec_rcv: "
"got packet with esplen = %d "
"from %s -- should be on "
"ENC(%d) octet boundary, "
"packet dropped\n",
irs->ilen,
irs->ipsaddr_txt,
ipsp->ips_encalg);
#endif
if(irs->stats){
irs->stats->rx_errors++;
}
returnIPSEC_RCV_BAD_DECRYPT;
}
ESP_DMP("postdecrypt",idat, irs->ilen);
irs->next_header= idat[irs->ilen - 1];
padlen= idat[irs->ilen - 2];
pad= padlen + 2 + irs->authlen;
KLIPS_PRINT(debug_rcv& DB_RX_IPAD,
"klips_debug:ipsec_rcv: "
"padlen=%d, contents:0x<offset>: 0x<value> 0x<value> ...\n",
padlen);
for(i = 1; i <= padlen; i++) {
if((i% 16) == 1) {
KLIPS_PRINT(debug_rcv& DB_RX_IPAD,
"klips_debug: %02x:",
i - 1);
}
KLIPS_PRINTMORE(debug_rcv& DB_RX_IPAD,
"%02x",
idat[irs->ilen- 2 - padlen + i - 1]);
if(i!= idat[irs->ilen - 2 - padlen + i - 1]) {
badpad= 1;
}
if((i% 16) == 0) {
KLIPS_PRINTMORE(debug_rcv& DB_RX_IPAD,
"\n");
}
}
if((i% 16) != 1) {
KLIPS_PRINTMORE(debug_rcv& DB_RX_IPAD,
"\n");
}
if(badpad){
KLIPS_PRINT(debug_rcv& DB_RX_IPAD,
"klips_debug:ipsec_rcv: "
"warning, decrypted packet from %s hasbad padding\n",
irs->ipsaddr_txt);
KLIPS_PRINT(debug_rcv& DB_RX_IPAD,
"klips_debug:ipsec_rcv: "
"...may be bad decryption -- notdropped\n");
ipsp->ips_errs.ips_encpad_errs+= 1;
}
KLIPS_PRINT(debug_rcv& DB_RX_IPAD,
"klips_debug:ipsec_rcv: "
"packet decrypted from %s: next_header= %d, padding = %d\n",
irs->ipsaddr_txt,
irs->next_header,
pad - 2 - irs->authlen);
irs->ipp->tot_len= htons(ntohs(irs->ipp->tot_len) - (esphlen + pad));
/*
* move the IP header forward by the size ofthe ESP header, which
* will remove the the ESP header from thepacket.
*
* XXX this is really unnecessary, since oddswe are in tunnel
* mode, and we will be *removing* this IP header.
*
*/
memmove((void*)(idat - irs->iphlen),
(void*)(skb->nh.raw), irs->iphlen);
ESP_DMP("esppostmove", (idat - irs->iphlen),
irs->iphlen+ irs->ilen);
/*skb_pull below, will move up by esphlen */
/*XXX not clear how this can happen, as the message indicates */
if(skb->len< esphlen) {
printk(KERN_WARNING
"klips_error:ipsec_rcv: "
"tried to skb_pull esphlen=%d, %davailable. This should never happen,please report.\n",
esphlen, (int)(skb->len));
returnIPSEC_RCV_ESP_DECAPFAIL;
}
skb_pull(skb,esphlen);
skb->nh.raw= idat - irs->iphlen;
irs->ipp= skb->nh.iph;
ESP_DMP("esppostpull", skb->data, skb->len);
/*now, trip off the padding from the end */
KLIPS_PRINT(debug_rcv& DB_RX_PKTRX,
"klips_debug:ipsec_rcv: "
"trimming to %d.\n",
irs->len - esphlen - pad);
if(pad+ esphlen <= irs->len) {
skb_trim(skb,irs->len - esphlen - pad);
}else {
KLIPS_PRINT(debug_rcv& DB_RX_PKTRX,
"klips_debug:ipsec_rcv: "
"bogus packet, size is zero ornegative, dropping.\n");
returnIPSEC_RCV_DECAPFAIL;
}
returnIPSEC_RCV_OK;
}