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

Linux内核中的IPSEC实现(3)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
5. 安全策略(xfrm_policy)处理

本节所介绍的函数都在net/xfrm/xfrm_policy.c中定义。

5.1 策略分配

策略分配函数为xfrm_policy_alloc(), 该函数被pfkey_spdadd()函数调用

struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp)
{
 struct xfrm_policy *policy;
// 分配struct xfrm_policy结构空间并清零
 policy = kzalloc(sizeof(struct xfrm_policy), gfp);
 if (policy) {
// 初始化链接节点
  INIT_HLIST_NODE(&policy->bydst);
  INIT_HLIST_NODE(&policy->byidx);
// 初始化锁
  rwlock_init(&policy->lock);
// 策略引用计数初始化为1
  atomic_set(&policy->refcnt, 1);
// 初始化定时器
  init_timer(&policy->timer);
  policy->timer.data = (unsigned long)policy;
  policy->timer.function = xfrm_policy_timer;
 }
 return policy;
}
EXPORT_SYMBOL(xfrm_policy_alloc);

定时器函数:
static void xfrm_policy_timer(unsigned long data)
{
 struct xfrm_policy *xp = (struct xfrm_policy*)data;
 unsigned long now = (unsigned long)xtime.tv_sec;
 long next = LONG_MAX;
 int warn = 0;
 int dir;
// 加锁
 read_lock(&xp->lock);
// 如果策略已经是死的, 退出
 if (xp->dead)
  goto out;
// 根据策略索引号确定策略处理的数据的方向, 看索引号的后3位
 dir = xfrm_policy_id2dir(xp->index);
// 如果到期了还要强制要增加一些时间
 if (xp->lft.hard_add_expires_seconds) {
// 计算强制增加的超时时间
  long tmo = xp->lft.hard_add_expires_seconds +
   xp->curlft.add_time - now;
// 没法增加超时了, 到期
  if (tmo <= 0)
   goto expired;
  if (tmo < next)
   next = tmo;
 }
// 如果到期了还要强制要增加的使用时间
 if (xp->lft.hard_use_expires_seconds) {
// 计算强制增加的使用时间
  long tmo = xp->lft.hard_use_expires_seconds +
   (xp->curlft.use_time ? : xp->curlft.add_time) - now;
// 没法增加超时了, 到期
  if (tmo <= 0)
   goto expired;
  if (tmo < next)
   next = tmo;
 }
// 如果到期了还要软性要增加一些时间
 if (xp->lft.soft_add_expires_seconds) {
// 计算软性增加的时间
  long tmo = xp->lft.soft_add_expires_seconds +
   xp->curlft.add_time - now;
// 软性增加超时小于0, 设置报警标志, 并将超时设置为XFRM_KM_TIMEOUT, 这点和其他不同
  if (tmo <= 0) {
   warn = 1;
   tmo = XFRM_KM_TIMEOUT;
  }
  if (tmo < next)
   next = tmo;
 }
// 如果到期了还要软性要增加的使用时间
 if (xp->lft.soft_use_expires_seconds) {
// 计算软性增加的使用时间
  long tmo = xp->lft.soft_use_expires_seconds +
   (xp->curlft.use_time ? : xp->curlft.add_time) - now;
// 软性增加超时小于0, 设置报警标志, 并将超时设置为XFRM_KM_TIMEOUT, 这点和其他不同
  if (tmo <= 0) {
   warn = 1;
   tmo = XFRM_KM_TIMEOUT;
  }
  if (tmo < next)
   next = tmo;
 }
// 需要报警, 调用到期回调
 if (warn)
  km_policy_expired(xp, dir, 0, 0);
// 如果更新的超时值有效, 修改定时器超时, 增加策略使用计数
 if (next != LONG_MAX &&
     !mod_timer(&xp->timer, jiffies + make_jiffies(next)))
  xfrm_pol_hold(xp);
out:
 read_unlock(&xp->lock);
 xfrm_pol_put(xp);
 return;
expired:
 read_unlock(&xp->lock);
// 如果确实到期, 删除策略
 if (!xfrm_policy_delete(xp, dir))
// 1表示是硬性到期了
  km_policy_expired(xp, dir, 1, 0);
 xfrm_pol_put(xp);
}
 
5.2 策略插入

策略插入函数为xfrm_policy_insert(), 该函数被pfkey_spdadd()函数调用, 注意策略链表是按优先权大小进行排序的有序链表, 因此插入策略时要进行优先权比较后插入到合适的位置.

int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
{
 struct xfrm_policy *pol;
 struct xfrm_policy *delpol;
 struct hlist_head *chain;
 struct hlist_node *entry, *newpos, *last;
 struct dst_entry *gc_list;
 write_lock_bh(&xfrm_policy_lock);
// 找到具体的hash链表
 chain = policy_hash_bysel(&policy->selector, policy->family, dir);
 delpol = NULL;
 newpos = NULL;
 last = NULL;
// 遍历链表, 该链表是以策略的优先级值进行排序的链表, 因此需要根据新策略的优先级大小
// 将新策略插到合适的位置
 hlist_for_each_entry(pol, entry, chain, bydst) {
// delpol要为空
  if (!delpol &&
// 策略类型比较
      pol->type == policy->type &&
// 选择子比较
      !selector_cmp(&pol->selector, &policy->selector) &&
// 安全上下文比较
      xfrm_sec_ctx_match(pol->security, policy->security)) {
// 新策略和已有的某策略匹配
   if (excl) {
// 如果是排他性添加操作, 要插入的策略在数据库中已经存在, 发生错误
    write_unlock_bh(&xfrm_policy_lock);
    return -EEXIST;
   }
// 保存好要删除的策略位置
   delpol = pol;
// 要更新的策略优先级值大于原有的优先级值, 重新循环找到合适