日期:2014-05-16  浏览次数:20569 次

Linux内核中的IPSEC实现(6)
本文档的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;
};

// 该函数被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;
 /* null auth can use a zero length key */
// 认证算法密钥长度要大于512
 if (x->aalg->alg_key_len > 512)
  goto error;
// 如果要进行UDP封装(进行NAT穿越), 错误, 因为AH是不支持NAT的
 if (x->encap)
  goto error;
// 分配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))
  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 (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(