本文档的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;
// 要更新的策略优先级值大于原有的优先级值, 重新循环找到合适的插入位置
// 因为这个链表是以优先级值进行排序的, 不能乱
// 现在delpol已经非空了,  前面的策略查找条件已经不可能满足了
   if (policy->priority > pol->priority)
    continue;
  } else if (policy->priority >= pol->priority) {
// 如果新的优先级不低于当前的优先级, 保存当前节点, 继续查找合适插入位置
   last = &pol->bydst;
   continue;
  }
// 这里是根据新策略的优先级确定的插入位置
  if (!newpos)
   newpos = &pol->bydst;
// 如果已经找到要删除的策略, 中断
  if (delpol)
   break;
  last = &pol->bydst;
 }
 if (!newpos)
  newpos = last;
// 插入策略到按目的地址HASH的链表的指定位置
 if (newpos)
  hlist_add_after(newpos, &policy->bydst);
 else
  hlist_add_head(&policy->bydst, chain);
// 增加策略引用计数
 xfrm_pol_hold(policy);
// 该方向的策略数增1
 xfrm_policy_count[dir]++;
 atomic_inc(&flow_cache_genid);
// 如果有相同的老策略, 要从目的地址HASH和索引号HASH这两个表中删除
 if (delpol) {
  hlist_del(&delpol->bydst);
  hlist_del(&delpol->byidx);
  xfrm_policy_count[dir]--;
 }
// 获取策略索引号, 插入索引HASH链表
 policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir);
 hlist_add_head(&policy->byidx, xfrm_policy_byidx+idx_hash(policy->index));
// 策略插入实际时间
 policy->curlft.add_time = (unsigned long)xtime.tv_sec;
 policy->curlft.use_time = 0;
 if (!mod_timer(&policy->timer, jiffies + HZ))
  xfrm_pol_hold(policy);
 write_unlock_bh(&xfrm_policy_lock);
// 释放老策略
 if (delpol)
  xfrm_policy_kill(delpol);
 else if (xfrm_bydst_should_resize(dir, NULL))
  schedule_work(&xfrm_hash_work);
// 下面释放所有策略当前的路由cache
 read_lock_bh(&xfrm_policy_lock);
 gc_list = NULL;
 entry = &policy->bydst;
// 遍历链表, 搜集垃圾路由cache建立链表
 hlist_for_each_entry_continue(policy, entry, bydst) {
  struct dst_entry *dst;
write_lock(&policy->lock);
// 策略的路由链表头
  dst = policy->bundles;
  if (dst) {
// 直接将整个策略路由链表加到垃圾链表前面
   struct dst_entry *tail = dst;
   while (tail->next)
    tail = tail->next;
   tail->next = gc_list;
   gc_list = dst;
// 当前策略的路由为空
   policy->bundles = NULL;
  }
  write_unlock(&policy->lock);
 }
 read_unlock_bh(&xfrm_policy_lock);
// 释放垃圾路由cahce
 while (gc_list) {
  struct dst_entry *dst = gc_list;
gc_list = dst->next;
  dst_free(dst);
 }
return 0;
}
EXPORT_SYMBOL(xfrm_policy_insert);
5.3  删除某类型的全部安全策略

该函数被pfkey_spdflush()等函数调用

void xfrm_policy_flush(u8 type)
{
 int dir;
write_lock_bh(&xfrm_policy_lock);
 for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
  struct xfrm_policy *pol;
  struct hlist_node *entry;
  int i, killed;
killed = 0;
 again1:
// 遍历inexact HASH链表
  hlist_for_each_entry(pol, entry,
         &xfrm_policy_inexact[dir], bydst) {
// 判断类型
   if (pol->type != type)
    continue;
// 将策略从bydst链表中断开
   hlist_del(&pol->bydst);
// 将策略从byidt链表中断开
   hlist_del(&pol->byidx);
   write_unlock_bh(&xfrm_policy_lock);
// 将策略状态置为dead, 并添加到系统的策略垃圾链表进行调度处理准备删除
   xfrm_policy_kill(pol);
   killed++;
write_lock_bh(&xfrm_policy_lock);
   goto again1;
  }
// 遍历所有目的HASH链表
  for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
 again2:
// 遍历按目的地址HASH的链表
   hlist_for_each_entry(pol, entry,
          xfrm_policy_bydst[dir].table + i,
          bydst) {
    if (pol->type != type)
     continue;
// 将节点从链表中断开
    hlist_del(&pol->bydst);
    hlist_del(&pol->byidx);
    write_unlock_bh(&xfrm_policy_lock);
// 释放节点
    xfrm_policy_kill(pol);
    killed++;
write_lock_bh(&xfrm_policy_lock);
    goto again2;
   }
  }
xfrm_policy_count[dir] -= killed;
 }
 atomic_inc(&flow_cache_genid);
 write_unlock_bh(&xfrm_policy_lock);
}
EXPORT_SYMBOL(xfrm_policy_flush);
/* Rule must be locked. Release descentant resources, announce
 * entry dead. The rule must be unlinked from lists to the moment.
 */
// 策略释放到垃圾链表
static void xfrm_policy_kill(struct xfrm_policy *policy)
{
 int dead;
write_lock_bh(&policy->lock);
// 保留老的DEAD标志
 dead = policy->dead;
// 设置策略DEAD标志
 policy->dead = 1;
 write_unlock_bh(&policy->lock);
// 为什么不在前面判断DEAD呢?
 if (unlikely(dead)) {
  WARN_ON(1);
  return;
 }
spin_lock(&xfrm_policy_gc_lock);
// 将该策略节点从当前链表断开, 插入策略垃圾链表
 hlist_add_head(&policy->bydst, &xfrm_policy_gc_list);
 spin_unlock(&xfrm_policy_gc_lock);
// 调度策略垃圾策略工作结构
 schedule_work(&xfrm_policy_gc_work);
}

5.4 策略查找

5.4.1 策略查找并删除

根据选择子和安全上下文查找策略, 可查找策略并删除, 被pfkey_spddelete()函数调用

struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
       struct xfrm_selector *sel,
       struct xfrm_sec_ctx *ctx, int delete)
{
 struct xfrm_policy *pol, *ret;
 struct hlist_head *chain;
 struct hlist_node *entry;
write_lock_bh(&xfrm_policy_lock);
// 定位HASH表
 chain = policy_hash_bysel(sel, sel->family, dir);
 ret = NULL;
// 遍历链表
 hlist_for_each_entry(pol, entry, chain, bydst) {
// 根据类型, 选择子和上下文进行匹配
  if (pol->type == type &&
      !selector_cmp(sel, &pol->selector) &&
      xfrm_sec_ctx_match(ctx, pol->security)) {
   xfrm_pol_hold(pol);
   if (delete) {
// 要的删除话将策略节点从目的地址HASH链表和索引HASH链表中断开
    hlist_del(&pol->bydst);
    hlist_del(&pol->byidx);
    xfrm_policy_count[dir]--;
   }
   ret = pol;
   break;
  }
 }
 write_unlock_bh(&xfrm_policy_lock);
if (ret && delete) {
// 增加genid
  atomic_inc(&flow_cache_genid);
// 将策略状态置为dead, 并添加到系统的策略垃圾链表进行调度处理准备删除
  xfrm_policy_kill(ret);
 }
 return ret;
}
EXPORT_SYMBOL(xfrm_policy_bysel_ctx);

5.4.2 按索引号查找并删除

struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete)
{
 struct xfrm_policy *pol, *ret;
 struct hlist_head *chain;
 struct hlist_node *entry;
write_lock_bh(&xfrm_policy_lock);
// 根据索引号定位链表
 chain = xfrm_policy_byidx + idx_hash(id);
 ret = NULL;
// 遍历链表
 hlist_for_each_entry(pol, entry, chain, byidx) {
// 策略的类型和索引号相同
  if (pol->type == type && pol->index == id) {
   xfrm_pol_hold(pol);
// 如果要删除, 将策略节点从链表中删除
   if (delete) {
    hlist_del(&pol->bydst);
    hlist_del(&pol->byidx);
    xfrm_policy_count[dir]--;
   }
   ret = pol;
   break;
  }
 }
 write_unlock_bh(&xfrm_policy_lock);
if (ret && delete) {
// 增加genid
  atomic_inc(&flow_cache_genid);
// 将策略状态置为dead, 并添加到系统的策略垃圾链表进行调度处理准备删除
  xfrm_policy_kill(ret);
 }
 return ret;
}
EXPORT_SYMBOL(xfrm_policy_byid);

5.4.3 根据路由查找策略

// 参数fl是路由相关的结构, 常用于路由查找中
// 注意返回值是整数, 0成功, 非0失败, 找到的策略通过参数objp进行传递
static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
          void **objp, atomic_t **obj_refp)
{
 struct xfrm_policy *pol;
 int err = 0;
#ifdef CONFIG_XFRM_SUB_POLICY
// 子策略查找, 属于Linux自己的扩展功能, 非标准功能
 pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir);
 if (IS_ERR(pol)) {
  err = PTR_ERR(pol);
  pol = NULL;
 }
 if (pol || err)
  goto end;
#endif
// 查找MAIN类型的策略
 pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir);
 if (IS_ERR(pol)) {
  err = PTR_ERR(pol);
  pol = NULL;
 }
#ifdef CONFIG_XFRM_SUB_POLICY
end:
#endif
// 将找到的策略赋值给objp返回
 if ((*objp = (void *) pol) != NULL)
  *obj_refp = &pol->refcnt;
 return err;
}

// 按类型查找策略
static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
           u16 family, u8 dir)
{
 int err;
 struct xfrm_policy *pol, *ret;
 xfrm_address_t *daddr, *saddr;
 struct hlist_node *entry;
 struct hlist_head *chain;
 u32 priority = ~0U;
// 由流结构的目的和源地址
 daddr = xfrm_flowi_daddr(fl, family);
 saddr = xfrm_flowi_saddr(fl, family);
 if (unlikely(!daddr || !saddr))
  return NULL;
read_lock_bh(&xfrm_policy_lock);
// 根据地址信息查找HASH链表
 chain = policy_hash_direct(daddr, saddr, family, dir);
 ret = NULL;
// 循环HASH链表
 hlist_for_each_entry(pol, entry, chain, bydst) {
// 检查流结构,类型和协议族是否匹配策略, 返回0表示匹配
  err = xfrm_policy_match(pol, fl, type, family, dir);
  if (err) {
   if (err == -ESRCH)
    continue;
   else {
    ret = ERR_PTR(err);
    goto fail;
   }
  } else {
// 备份找到的策略和优先级
   ret = pol;
   priority = ret->priority;
   break;
  }
 }
// 再在inexact链表中查找策略, 如果也找到策略, 而且优先级更小,
// 将新找到的策略替代前面找到的策略
 chain = &xfrm_policy_inexact[dir];
// 循环HASH链表
 hlist_for_each_entry(pol, entry, chain, bydst) {
// 检查流结构,类型和协议族是否匹配策略, 返回0表示匹配
  err = xfrm_policy_match(pol, fl, type, family, dir);
  if (err) {
   if (err == -ESRCH)
    continue;
   else {
    ret = ERR_PTR(err);
    goto fail;
   }
  } else if (pol->priority < priority) {
// 如果新找到的策略优先级更小, 将其取代原来找到的策略
   ret = pol;
   break;
  }
 }
 if (ret)
  xfrm_pol_hold(ret);
fail:
 read_unlock_bh(&xfrm_policy_lock);
return ret;
}
// 检查xfrm策略是否和流参数匹配
// 返回0表示匹配成功
static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
        u8 type, u16 family, int dir)
{
// 选择子
 struct xfrm_selector *sel = &pol->selector;
 int match, ret = -ESRCH;
// 检查策略协议族和类型是否匹配
 if (pol->family != family ||
     pol->type != type)
  return ret;
// 检查选择子是否匹配, 返回非0值表示匹配成功
 match = xfrm_selector_match(sel, fl, family);
 if (match)
// 这种security函数可以不用考虑, 当作返回0的函数即可
  ret = security_xfrm_policy_lookup(pol, fl->secid, dir);
return ret;
}
// 选择子匹配,分别对IPV4和IPV6协议族比较
static inline int
xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
      unsigned short family)
{
 switch (family) {
 case AF_INET:
  return __xfrm4_selector_match(sel, fl);
 case AF_INET6:
  return __xfrm6_selector_match(sel, fl);
 }
 return 0;
}
//IPV4协议族选择子比较
static inline int
__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
{
// 比较V4目的地址, V4源地址, 目的端口, 源端口, 协议, 网卡索引号
 return  addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) &&
  addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) &&
  !((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&
  !((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&
  (fl->proto == sel->proto || !sel->proto) &&
  (fl->oif == sel->ifindex || !sel->ifindex);
}
//IPV6协议族选择子比较
static inline int
__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
{
// 比较V6目的地址, V6源地址, 目的端口, 源端口, 协议, 网卡索引号
 return  addr_match(&fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&
  addr_match(&fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&
  !((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&
  !((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&
  (fl->proto == sel->proto || !sel->proto) &&
  (fl->oif == sel->ifindex || !sel->ifindex);
}

5.4.4 查找和sock对应的策略
static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl)
{
 struct xfrm_policy *pol;
read_lock_bh(&xfrm_policy_lock);
// sock结构中有sk_policy用来指向双向数据的安全策略
 if ((pol = sk->sk_policy[dir]) != NULL) {
// 检查该策略的选择子是否和流结构匹配
   int match = xfrm_selector_match(&pol->selector, fl,
      sk->sk_family);
   int err = 0;
// 如果匹配的话将策略作为结果返回
  if (match) {
// 这个security函数可视为返回0的空函数
   err = security_xfrm_policy_lookup(pol, fl->secid,
     policy_to_flow_dir(dir));
   if (!err)
    xfrm_pol_hold(pol);
   else if (err == -ESRCH)
    pol = NULL;
   else
    pol = ERR_PTR(err);
  } else
   pol = NULL;
 }
 read_unlock_bh(&xfrm_policy_lock);
 return pol;
}

5.5 遍历安全策略

该函数被pfkey_spddump()等函数中调用

// func函数用来指定对遍历的策略进行的查找
// 实际遍历了两次所有策略
int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*),
       void *data)
{
 struct xfrm_policy *pol;
 struct hlist_node *entry;
 int dir, count, error;
read_lock_bh(&xfrm_policy_lock);
 count = 0;
// 先统计符合类型的策略的总数量, 方向是双向的
 for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) {
  struct hlist_head *table = xfrm_policy_bydst[dir].table;
  int i;
// inexact HASH表
  hlist_for_each_entry(pol, entry,
         &xfrm_policy_inexact[dir], bydst) {
   if (pol->type == type)
    count++;
  }
// 遍历按地址HASH的链表
  for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
// 遍历链表
   hlist_for_each_entry(pol, entry, table + i, bydst) {
    if (pol->type == type)
     count++;
   }
  }
 }
if (count == 0) {
  error = -ENOENT;
  goto out;
 }
// 重新遍历HASH表, 当前的count值作为SA的序号, 因此用户空间收到的序号是递减的
 for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) {
  struct hlist_head *table = xfrm_policy_bydst[dir].table;
  int i;
// 遍历inexact链表
  hlist_for_each_entry(pol, entry,
         &xfrm_policy_inexact[dir], bydst) {
   if (pol->type != type)
    continue;
// 对符合类型的策略调用func函数
   error = func(pol, dir % XFRM_POLICY_MAX, --count, data);
   if (error)
    goto out;
  }
// 遍历按地址HASH的链表
  for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
   hlist_for_each_entry(pol, entry, table + i, bydst) {
    if (pol->type != type)
     continue;
// 对符合类型的策略调用func函数, 当count递减到0时表示是最后一个策略了
    error = func(pol, dir % XFRM_POLICY_MAX, --count, data);
    if (error)
     goto out;
   }
  }
 }
 error = 0;
out:
 read_unlock_bh(&xfrm_policy_lock);
 return error;
}
EXPORT_SYMBOL(xfrm_policy_walk);
5.5 策略检查

__xfrm_policy_check函数也是一个比较重要的函数, 被xfrm_policy_check()调用, 又被xfrm4_policy_check()和xfrm6_policy_check()调用, 而这两个函数在网络层的输入和转发处调用.
对普通包就返回合法, 对IPSEC包检查策略是否合法, 是否和路由方向匹配

// 返回1表示合法, 0表示不合法, 对于该函数返回0的数据包通常是被丢弃
int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
   unsigned short family)
{
 struct xfrm_policy *pol;
 struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
 int npols = 0;
 int xfrm_nr;
 int pi;
 struct flowi fl;
// 将策略方向转换为流方向, 其实值是一样的
 u8 fl_dir = policy_to_flow_dir(dir);
 int xerr_idx = -1;
// 调用协议族的decode_session()函数, 对IPV4来说就是_decode_session4
// 将skb中的地址端口等信息填入流结构fl中
 if (xfrm_decode_session(skb, &fl, family) < 0)
  return 0;
// 如果内核支持NETFILTER, 将调用ip_nat_decode_session函数填写NAT信息
// 否则的话就是个空函数
 nf_nat_decode_session(skb, &fl, family);
/* First, check used SA against their selectors. */
 if (skb->sp) {
// 该包是进行了解密后的IPSEC包
  int i;
for (i=skb->sp->len-1; i>=0; i--) {
// 获取该包相关的SA信息
   struct xfrm_state *x = skb->sp->xvec[i];
// 检查SA选择子和流参数(路由)是否匹配, 结果为0表示不匹配, 不匹配的话返回
   if (!xfrm_selector_match(&x->sel, &fl, family))
    return 0;
  }
 }
pol = NULL;
// 如果sock结构中有策略
 if (sk && sk->sk_policy[dir]) {
// 检查策略是否和流结构匹配, 匹配的话返回策略
  pol = xfrm_sk_policy_lookup(sk, dir, &fl);
  if (IS_ERR(pol))
   return 0;
 }
// 查找路由信息, 如果没有就创建路由, xfrm_policy_lookup()函数作为参数传递给
// flow_cache_lookup()函数, 查找和该路由对应的安全策略
 if (!pol)
  pol = flow_cache_lookup(&fl, family, fl_dir,
     xfrm_policy_lookup);
// 查找过程中出错,返回0
 if (IS_ERR(pol))
  return 0;
// 策略不存在
 if (!pol) {
// 如果该包是IPSEC包而且安全路径中的SA不是传输模式,
// 转发时, 对于已经封装的包没必要再次封装;
// 输入时, 是自身的IPSEC通信包封装基本也无意义
  if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
// 拒绝该安全路径, 返回0失败
   xfrm_secpath_reject(xerr_idx, skb, &fl);
   return 0;
  }
// 普通包处理, 安全策略不存在, 返回1
  return 1;
 }
// 找到安全策略, 对该包要根据策略进行IPSEC处理
// 更新策略当前使用时间
 pol->curlft.use_time = (unsigned long)xtime.tv_sec;
pols[0] = pol;
 npols ++;
#ifdef CONFIG_XFRM_SUB_POLICY
// 如果定义了子策略的话极限查找子策略, 这是标准IPSEC中没定义的, 可以不考虑
 if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
  pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
          &fl, family,
          XFRM_POLICY_IN);
  if (pols[1]) {
   if (IS_ERR(pols[1]))
    return 0;
   pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec;
   npols ++;
  }
 }
#endif
// 策略动作是允许通过
 if (pol->action == XFRM_POLICY_ALLOW) {
  struct sec_path *sp;
// 先伪造个安全路径
  static struct sec_path dummy;
  struct xfrm_tmpl *tp[XFRM_MAX_DEPTH];
  struct xfrm_tmpl *stp[XFRM_MAX_DEPTH];
  struct xfrm_tmpl **tpp = tp;
  int ti = 0;
  int i, k;
// 如果数据包没有安全路径, 路径指针初始化为伪造的安全路径
  if ((sp = skb->sp) == NULL)
   sp = &dummy;
// 遍历策略数组, 包括主策略和子策略(内核支持子策略的话),一般情况下就一个策略
  for (pi = 0; pi < npols; pi++) {
// 如果有非允许通过的其他安全策略, 放弃
   if (pols[pi] != pol &&
       pols[pi]->action != XFRM_POLICY_ALLOW)
    goto reject;
// 如果策略层次太多, 放弃
   if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH)
    goto reject_error;
// 备份策略中的xfrm向量模板, ti是数量
   for (i = 0; i < pols[pi]->xfrm_nr; i++)
    tpp[ti++] = &pols[pi]->xfrm_vec[i];
  }
// 策略数量
  xfrm_nr = ti;
  if (npols > 1) {
// 如果超过一个策略,进行排序, 只是在内核支持子系统时才用, 否则只是返回错误
// 但该错误可以忽略
   xfrm_tmpl_sort(stp, tpp, xfrm_nr, family);
   tpp = stp;
  }
/* For each tunnel xfrm, find the first matching tmpl.
   * For each tmpl before that, find corresponding xfrm.
   * Order is _important_. Later we will implement
   * some barriers, but at the moment barriers
   * are implied between each two transformations.
   */
// 遍历检查策略模板是否OK
  for (i = xfrm_nr-1, k = 0; i >= 0; i--) {
// 注意k既是输入, 也是输出值, k初始化为0
// 返回值大于等于0表示策略合法可用
   k = xfrm_policy_ok(tpp[i], sp, k, family);
   if (k < 0) {
    if (k < -1)
     /* "-2 - errored_index" returned */
     xerr_idx = -(2+k);
    goto reject;
   }
  }
// 存在非传输模式的策略, 放弃
  if (secpath_has_nontransport(sp, k, &xerr_idx))
   goto reject;
xfrm_pols_put(pols, npols);
  return 1;
 }
// 放弃, 返回0表示检查不通过
reject:
 xfrm_secpath_reject(xerr_idx, skb, &fl);
reject_error:
 xfrm_pols_put(pols, npols);
 return 0;
}
EXPORT_SYMBOL(__xfrm_policy_check);

/*
 * 0 or more than 0 is returned when validation is succeeded (either bypass
 * because of optional transport mode, or next index of the mathced secpath
 * state with the template.
 * -1 is returned when no matching template is found.
 * Otherwise "-2 - errored_index" is returned.
 */
static inline int
xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
        unsigned short family)
{
 int idx = start;
if (tmpl->optional) {
// 如果是传输模式, 直接返回
  if (tmpl->mode == XFRM_MODE_TRANSPORT)
   return start;
 } else
  start = -1;
 for (; idx < sp->len; idx++) {
// sp->xvec是xfrm状态
// 如果安全路径和模板匹配,返回索引位置
  if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
   return ++idx;
// 如果安全路径中的SA不是传输模式,返回错误
  if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) {
   if (start == -1)
    start = -2-idx;
   break;
  }
 }
 return start;
}

5.6 安全策略路由查找
xfrm_lookup函数是个非常重要的函数, 用来根据安全策略构造数据包的路由项链表, 该路由项链表反映了对数据包进行IPSEC封装的多层次的处理, 每封装一次, 就增加一个路由项.
该函数被路由查找函数ip_route_output_flow()调用, 针对的是转发或发出的数据包.

/* Main function: finds/creates a bundle for given flow.
 *
 * At the moment we eat a raw IP route. Mostly to speed up lookups
 * on interfaces with disabled IPsec.
 */
// 返回0表示超过, 负数表示失败
int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
  struct sock *sk, int flags)
{
 struct xfrm_policy *policy;
 struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
 int npols;
 int pol_dead;
 int xfrm_nr;
 int pi;
 struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
 struct dst_entry *dst, *dst_orig = *dst_p;
 int nx = 0;
 int err;
 u32 genid;
 u16 family;
 u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
restart:
// 初始化清零操作
 genid = atomic_read(&flow_cache_genid);
 policy = NULL;
 for (pi = 0; pi < ARRAY_SIZE(pols); pi++)
  pols[pi] = NULL;
 npols = 0;
 pol_dead = 0;
 xfrm_nr = 0;
if (sk && sk->sk_policy[1]) {
// 如果在sock中定义了安全策略, 查找该sock相关的策略
// 一个socket的安全策略可通过setsockopt()设置, socket选项为
// IP_IPSEC_POLICY或IP_XFRM_POLICY(net/ipv4/ip_sockglue.c)
  policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
  if (IS_ERR(policy))
   return PTR_ERR(policy);
 }
if (!policy) {
// 没找到sock自身定义的安全策略
  /* To accelerate a bit...  */
// 如果初始路由中设置了非IPSEC标志或没有发出方向的安全策略, 直接返回
  if ((dst_orig->flags & DST_NOXFRM) ||
      !xfrm_policy_count[XFRM_POLICY_OUT])
   return 0;
// 查找路由信息, 如果没有就创建路由, xfrm_policy_lookup()函数作为参数传递给
// flow_cache_lookup()函数, 查找和该路由对应的安全策略
  policy = flow_cache_lookup(fl, dst_orig->ops->family,
        dir, xfrm_policy_lookup);
  if (IS_ERR(policy))
   return PTR_ERR(policy);
 }
// 找不到策略的话返回, 就是普通包普通路由项
 if (!policy)
  return 0;
// 以下是存在安全策略的情况, 要对该包建立安全路由链表
// 初始路由的协议族
 family = dst_orig->ops->family;
// 安全策略最近使用时间
 policy->curlft.use_time = (unsigned long)xtime.tv_sec;
// 将找到的策略作为策略数组的第一项
 pols[0] = policy;
 npols ++;
 xfrm_nr += pols[0]->xfrm_nr;
// 根据策略操作结果进行相关处理, 只有两种情况: 阻塞或通过
 switch (policy->action) {
 case XFRM_POLICY_BLOCK:
// 阻塞该数据包, 返回错误
  /* Prohibit the flow */
  err = -EPERM;
  goto error;
case XFRM_POLICY_ALLOW:
// 允许该包通过, 这样就要对该包进行IPSEC处理
#ifndef CONFIG_XFRM_SUB_POLICY
// 对子策略操作忽略
  if (policy->xfrm_nr == 0) {
   /* Flow passes not transformed. */
   xfrm_pol_put(policy);
   return 0;
  }
#endif
/* Try to find matching bundle.
   *
   * LATER: help from flow cache. It is optional, this
   * is required only for output policy.
   */
// 查找是否已经存在安全路由, bundle可以理解为描述安全处理的安全路由, 数据包走该路由
// 就是进行某种安全封装, 和普通路由项一样, 用过的安全路由也被缓存起来
  dst = xfrm_find_bundle(fl, policy, family);
  if (IS_ERR(dst)) {
   err = PTR_ERR(dst);
   goto error;
  }
// 如果找到安全路由, 退出switch
  if (dst)
   break;
#ifdef CONFIG_XFRM_SUB_POLICY
// 对子策略操作, 由于是非标准IPSEC,忽略
  if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
   pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
           fl, family,
           XFRM_POLICY_OUT);
   if (pols[1]) {
    if (IS_ERR(pols[1])) {
     err = PTR_ERR(pols[1]);
     goto error;
    }
    if (pols[1]->action == XFRM_POLICY_BLOCK) {
     err = -EPERM;
     goto error;
    }
    npols ++;
    xfrm_nr += pols[1]->xfrm_nr;
   }
  }
/*
   * Because neither flowi nor bundle information knows about
   * transformation template size. On more than one policy usage
   * we can realize whether all of them is bypass or not after
   * they are searched. See above not-transformed bypass
   * is surrounded by non-sub policy configuration, too.
   */
  if (xfrm_nr == 0) {
   /* Flow passes not transformed. */
   xfrm_pols_put(pols, npols);
   return 0;
  }
#endif
// 没找到安全路由, 准备构造新的路由项
// 利用策略, 流等参数构造相关SA(xfrm_state)保存在xfrm中, nx为SA数量
  nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
if (unlikely(nx<0)) {
// nx<0表示失败, 没找到SA
// 但如果是-EAGAIN表示已经通知用户空间的IKE进行协商新的SA了,
// 目前只生成了ACQUIRE类型的xfrm_state
   err = nx;
   if (err == -EAGAIN && flags) {
// 进程进入阻塞状态
    DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&km_waitq, &wait);
    set_current_state(TASK_INTERRUPTIBLE);
    schedule();
    set_current_state(TASK_RUNNING);
    remove_wait_queue(&km_waitq, &wait);
// 阻塞解除, 重新解析SA
    nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
if (nx == -EAGAIN && signal_pending(current)) {
     err = -ERESTART;
     goto error;
    }
    if (nx == -EAGAIN ||
        genid != atomic_read(&flow_cache_genid)) {
     xfrm_pols_put(pols, npols);
     goto restart;
    }
    err = nx;
   }
   if (err < 0)
    goto error;
  }
  if (nx == 0) {
// nx==0表示数据是不需要进行IPSEC处理的, 返回
   /* Flow passes not transformed. */
   xfrm_pols_put(pols, npols);
   return 0;
  }
// 保存初始路由
  dst = dst_orig;
// 创建新的安全路由, 返回0 表示成功, 失败返回负数
// dst在成功返回时保存安全路由项, 每个SA处理对应一个安全路由, 这些安全路由通过
// 路由项中的child链接为一个链表, 这样就可以对数据包进行连续变换, 如先压缩,
// 再ESP封装, 再AH封装等.
// 路由项链表的构造和协议族相关, 后续文章中介绍具体协议族中的实现时再详细描述
// 所构造出的路由项的具体结构情况
  err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, family);
if (unlikely(err)) {
// 失败的话释放刚获取的SA
   int i;
   for (i=0; i<nx; i++)
    xfrm_state_put(xfrm[i]);
   goto error;
  }
// 检查所有策略的dead状态
  for (pi = 0; pi < npols; pi++) {
   read_lock_bh(&pols[pi]->lock);
   pol_dead |= pols[pi]->dead;
   read_unlock_bh(&pols[pi]->lock);
  }
write_lock_bh(&policy->lock);
// 如果有策略是dead或获取的安全路由项有问题, 释放安全路由
  if (unlikely(pol_dead || stale_bundle(dst))) {
   /* Wow! While we worked on resolving, this
    * policy has gone. Retry. It is not paranoia,
    * we just cannot enlist new bundle to dead object.
    * We can't enlist stable bundles either.
    */
   write_unlock_bh(&policy->lock);
   if (dst)
    dst_free(dst);
err = -EHOSTUNREACH;
   goto error;
  }
// 将安全路由加入到策略的路由项链表头, 该链表是以NULL结尾的单向链表
// 不过一般情况下应该只有一个元素
  dst->next = policy->bundles;
  policy->bundles = dst;
  dst_hold(dst);
  write_unlock_bh(&policy->lock);
 }
// 将安全链表作为
 *dst_p = dst;
 dst_release(dst_orig);
  xfrm_pols_put(pols, npols);
 return 0;
error:
 dst_release(dst_orig);
 xfrm_pols_put(pols, npols);
 *dst_p = NULL;
 return err;
}
EXPORT_SYMBOL(xfrm_lookup);
以下是在xfrm_lookup中用到的两个bundle的操作函数: 查找和创建, 由于使用了地址参数, 是和协议族相关的, 因此具体实现是在各协议族中实现的, 在后续文章中介绍协议族中的xfrm实现时再详细介绍.
static struct dst_entry *
xfrm_find_bundle(struct flowi *fl, struct xfrm_policy *policy, unsigned short family)
{
 struct dst_entry *x;
 struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
 if (unlikely(afinfo == NULL))
  return ERR_PTR(-EINVAL);
 x = afinfo->find_bundle(fl, policy);
 xfrm_policy_put_afinfo(afinfo);
 return x;
}
/* Allocate chain of dst_entry's, attach known xfrm's, calculate
 * all the metrics... Shortly, bundle a bundle.
 */
static int
xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
     struct flowi *fl, struct dst_entry **dst_p,
     unsigned short family)
{
 int err;
 struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
 if (unlikely(afinfo == NULL))
  return -EINVAL;
 err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p);
 xfrm_policy_put_afinfo(afinfo);
 return err;
}

// 策略解析, 生成SA
static int
xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, struct flowi *fl,
    struct xfrm_state **xfrm,
    unsigned short family)
{
 struct xfrm_state *tp[XFRM_MAX_DEPTH];
// npols > 1是定义了子策略的情况, 这时用tp数组保存找到的SA, 但没法返回原函数中了
// 不明白为什么这么作
 struct xfrm_state **tpp = (npols > 1) ? tp : xfrm;
 int cnx = 0;
 int error;
 int ret;
 int i;
// 遍历策略, 一般情况下npols其实只是1
 for (i = 0; i < npols; i++) {
// 检查保存SA的缓冲区是否还够大
  if (cnx + pols[i]->xfrm_nr >= XFRM_MAX_DEPTH) {
   error = -ENOBUFS;
   goto fail;
  }
// 协议一个策略模板
  ret = xfrm_tmpl_resolve_one(pols[i], fl, &tpp[cnx], family);
  if (ret < 0) {
   error = ret;
   goto fail;
  } else
   cnx += ret;
 }
/* found states are sorted for outbound processing */
// 多个策略的话对找到的SA排序, 在没定义子策略的情况下是个空函数
 if (npols > 1)
  xfrm_state_sort(xfrm, tpp, cnx, family);
return cnx;
fail:
 for (cnx--; cnx>=0; cnx--)
  xfrm_state_put(tpp[cnx]);
 return error;
}
/* Resolve list of templates for the flow, given policy. */
static int
xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
        struct xfrm_state **xfrm,
        unsigned short family)
{
 int nx;
 int i, error;
// 从流结构中获取地址信息
 xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
 xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
 xfrm_address_t tmp;
// 遍历策略中的所有SA
 for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
  struct xfrm_state *x;
  xfrm_address_t *remote = daddr;
  xfrm_address_t *local  = saddr;
  struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
if (tmpl->mode == XFRM_MODE_TUNNEL) {
// 如果是通道模式, 会添加外部IP头, 内部IP头都封装在内部, 因此地址信息使用外部地址
// 即策略的SA模板中的地址信息
   remote = &tmpl->id.daddr;
   local = &tmpl->saddr;
// 如果local地址没定义, 选取个源地址作为本地地址, 选取过程是协议族相关的
   if (xfrm_addr_any(local, family)) {
    error = xfrm_get_saddr(&tmp, remote, family);
    if (error)
     goto fail;
    local = &tmp;
   }
  }
// 根据地址,流,策略等新查找SA(xfrm_state),如果找不到现成的会通知IKE程序进行协商
// 生成新的SA, 但生成可用SA前先返回ACQUIRE类型的SA, 见前一篇文章
  x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
if (x && x->km.state == XFRM_STATE_VALID) {
// 如果SA是合法, 保存
   xfrm[nx++] = x;
   daddr = remote;
   saddr = local;
   continue;
  }
  if (x) {
// x存在但不是VALID的, 只要不出错, 应该是ACQUIRE类型的, 等IKE进程协商结果, 返回-EAGAIN
   error = (x->km.state == XFRM_STATE_ERROR ?
     -EINVAL : -EAGAIN);
   xfrm_state_put(x);
  }
if (!tmpl->optional)
   goto fail;
 }
 return nx;
fail:
 for (nx--; nx>=0; nx--)
  xfrm_state_put(xfrm[nx]);
 return error;
}

关于路由处理过程在后面介绍IPSEC包的发出过程时会介绍路由处理过程, 从而了解安全路由的作用.
5.6 变更HASH表大小

改变策略状态表的是通过工作队列来实现的, 和xfrm_state类似

工作定义:
static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize, NULL);

// 更改HASH表大小
static void xfrm_hash_resize(void *__unused)
{
 int dir, total;
mutex_lock(&hash_resize_mutex);
total = 0;
// 注意策略都是双向的
 for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
// 按目的地址进行HASH的链表: 如果需要更改HASH表大小, 修改之
  if (xfrm_bydst_should_resize(dir, &total))
   xfrm_bydst_resize(dir);
 }
// 按索引号进行HASH的链表更新
 if (xfrm_byidx_should_resize(total))
  xfrm_byidx_resize(total);
mutex_unlock(&hash_resize_mutex);
}
// 检查按目的地址HASH的HASH链表
static inline int xfrm_bydst_should_resize(int dir, int *total)
{
// 该方向是策略的数量
 unsigned int cnt = xfrm_policy_count[dir];
// 该方向是策略的掩码
 unsigned int hmask = xfrm_policy_bydst[dir].hmask;
// 累加策略数量
 if (total)
  *total += cnt;
// 如果策略数量大于策略掩码量, 该增加了
 if ((hmask + 1) < xfrm_policy_hashmax &&
     cnt > hmask)
  return 1;
// 否则不用
 return 0;
}
// 检查按索引号HASH的HASH链表
static inline int xfrm_byidx_should_resize(int total)
{
 unsigned int hmask = xfrm_idx_hmask;
// 策略总量超过当前的索引号掩码, 该扩大了
 if ((hmask + 1) < xfrm_policy_hashmax &&
     total > hmask)
  return 1;
return 0;
}

// 更改按目的地址HASH的HASH链表大小
static void xfrm_bydst_resize(int dir)
{
// 该方向的HASH表掩码(最大值, 一般是2^N-1)
 unsigned int hmask = xfrm_policy_bydst[dir].hmask;
// 新HASH表掩码(2^(N+1)-1)
 unsigned int nhashmask = xfrm_new_hash_mask(hmask);
// 新HASH表大小
 unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
// 老HAHS表
 struct hlist_head *odst = xfrm_policy_bydst[dir].table;
// 新HASH表
 struct hlist_head *ndst = xfrm_hash_alloc(nsize);
 int i;
// 新HASH表空间分配不出来, 返回
 if (!ndst)
  return;
write_lock_bh(&xfrm_policy_lock);
// 将所有策略节点转到新HASH表
 for (i = hmask; i >= 0; i--)
  xfrm_dst_hash_transfer(odst + i, ndst, nhashmask);
// 将全局变量值更新为新HASH表参数
 xfrm_policy_bydst[dir].table = ndst;
 xfrm_policy_bydst[dir].hmask = nhashmask;
write_unlock_bh(&xfrm_policy_lock);
// 释放老HASH表参数
 xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));
}

// 更改按索引号HASH的HASH链表大小, 操作和上面类似
static void xfrm_byidx_resize(int total)
{
 unsigned int hmask = xfrm_idx_hmask;
 unsigned int nhashmask = xfrm_new_hash_mask(hmask);
 unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
 struct hlist_head *oidx = xfrm_policy_byidx;
 struct hlist_head *nidx = xfrm_hash_alloc(nsize);
 int i;
if (!nidx)
  return;
write_lock_bh(&xfrm_policy_lock);
for (i = hmask; i >= 0; i--)
  xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask);
xfrm_policy_byidx = nidx;
 xfrm_idx_hmask = nhashmask;
write_unlock_bh(&xfrm_policy_lock);
xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head));
}
5.7 垃圾搜集

垃圾搜集的是不用的安全路由项, 是和协议族相关的
afinfo->garbage_collect = __xfrm_garbage_collect;

// 就是xfrm_prune_bundles()函数的包装函数,条件是unused_bundle()函数定义
static void __xfrm_garbage_collect(void)
{
 xfrm_prune_bundles(unused_bundle);
}

// 删减安全路由
static void xfrm_prune_bundles(int (*func)(struct dst_entry *))
{
// 垃圾链表
 struct dst_entry *gc_list = NULL;
 int dir;
read_lock_bh(&xfrm_policy_lock);
// 循环所有方向
 for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
  struct xfrm_policy *pol;
  struct hlist_node *entry;
  struct hlist_head *table;
  int i;
// 遍历inexact链表
  hlist_for_each_entry(pol, entry,
         &xfrm_policy_inexact[dir], bydst)
// 如果节点满足条件就删除挂接到垃圾链表
   prune_one_bundle(pol, func, &gc_list);

// 遍历目的地址HASH的链表
  table = xfrm_policy_bydst[dir].table;
  for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
// 如果节点满足条件就删除挂接到垃圾链表
   hlist_for_each_entry(pol, entry, table + i, bydst)
    prune_one_bundle(pol, func, &gc_list);
  }
 }
 read_unlock_bh(&xfrm_policy_lock);
// 如果搜集到的垃圾, 释放安全路由
 while (gc_list) {
  struct dst_entry *dst = gc_list;
  gc_list = dst->next;
  dst_free(dst);
 }
}
// 没用的路由, 使用数为0
static int unused_bundle(struct dst_entry *dst)
{
 return !atomic_read(&dst->__refcnt);
}
// 删除单个路由
static void prune_one_bundle(struct xfrm_policy *pol, int (*func)(struct dst_entry *), struct dst_entry **gc_list_p)
{
 struct dst_entry *dst, **dstp;
// 策略写锁
 write_lock(&pol->lock);
// 策略的路由项链表起点
 dstp = &pol->bundles;
// 遍历链表
 while ((dst=*dstp) != NULL) {
  if (func(dst)) {
// 如果满足条件, 将节点从链表中删除, 添加到垃圾链表
   *dstp = dst->next;
   dst->next = *gc_list_p;
   *gc_list_p = dst;
  } else {
   dstp = &dst->next;
  }
 }
 write_unlock(&pol->lock);
}

5.8 杂项

这些杂项并不是策略的直接处理函数, 而是xfrm的一些相关处理, 只是也放在xfrm_policy.c中了.

5.8.1 协议处理类型处理
xfrm_type用来定义各种协议处理类型, 如AH,ESP, IPCOMP, IPIP等
// 登记协议处理类型, 返回0成功, 非0失败
int xfrm_register_type(struct xfrm_type *type, unsigned short family)
{
// 找到协议族相关的策略信息结构
 struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
 struct xfrm_type **typemap;
 int err = 0;
if (unlikely(afinfo == NULL))
  return -EAFNOSUPPORT;
// 策略信息结构中的类型数组
 typemap = afinfo->type_map;
// 如果数组中相应协议对应元素非空, 则赋值, 否则发生错误
 if (likely(typemap[type->proto] == NULL))
  typemap[type->proto] = type;
 else
  err = -EEXIST;
 xfrm_policy_unlock_afinfo(afinfo);
 return err;
}
EXPORT_SYMBOL(xfrm_register_type);

// 拆除协议处理类型, 返回0成功, 非0失败
int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
{
// 找到协议族相关的策略信息结构
 struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
 struct xfrm_type **typemap;
 int err = 0;
if (unlikely(afinfo == NULL))
  return -EAFNOSUPPORT;
// 策略信息结构中的类型数组
 typemap = afinfo->type_map;
// 如果数组中相应协议对应元素等于要删除的结构, 元素清空, 否则发生错误
 if (unlikely(typemap[type->proto] != type))
  err = -ENOENT;
 else
  typemap[type->proto] = NULL;
 xfrm_policy_unlock_afinfo(afinfo);
 return err;
}
EXPORT_SYMBOL(xfrm_unregister_type);
// 根据协议号和协议族查找类型
struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
{
 struct xfrm_policy_afinfo *afinfo;
 struct xfrm_type **typemap;
 struct xfrm_type *type;
 int modload_attempted = 0;
retry:
// 找到协议族相关的策略信息结构
 afinfo = xfrm_policy_get_afinfo(family);
 if (unlikely(afinfo == NULL))
  return NULL;
// 策略信息结构中的类型数组
 typemap = afinfo->type_map;
// 数组中对应指定协议的元素
 type = typemap[proto];
// 增加type模块的使用计数
 if (unlikely(type && !try_module_get(type->owner)))
  type = NULL;
// 如果当前type为空, 则加载type的内核模块, 重新查找
 if (!type && !modload_attempted) {
  xfrm_policy_put_afinfo(afinfo);
  request_module("xfrm-type-%d-%d",
          (int) family, (int) proto);
  modload_attempted = 1;
  goto retry;
 }
xfrm_policy_put_afinfo(afinfo);
 return type;
}
// 释放类型模块使用计数
void xfrm_put_type(struct xfrm_type *type)
{
 module_put(type->owner);
}
5.8.2 协议模式处理

模式目前包括通道和传输两种.

// 登记模式, 返回0成功, 非0失败
int xfrm_register_mode(struct xfrm_mode *mode, int family)
{
 struct xfrm_policy_afinfo *afinfo;
 struct xfrm_mode **modemap;
 int err;
if (unlikely(mode->encap >= XFRM_MODE_MAX))
  return -EINVAL;
// 找到协议族相关的策略信息结构
 afinfo = xfrm_policy_lock_afinfo(family);
 if (unlikely(afinfo == NULL))
  return -EAFNOSUPPORT;
err = -EEXIST;
// 策略信息结构中的模式数组
 modemap = afinfo->mode_map;
// 数组元素非空的话赋值, 返回成功
 if (likely(modemap[mode->encap] == NULL)) {
  modemap[mode->encap] = mode;
  err = 0;
 }
xfrm_policy_unlock_afinfo(afinfo);
 return err;
}
EXPORT_SYMBOL(xfrm_register_mode);
// 拆除模式, 返回0成功, 非0失败
int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
{
 struct xfrm_policy_afinfo *afinfo;
 struct xfrm_mode **modemap;
 int err;
if (unlikely(mode->encap >= XFRM_MODE_MAX))
  return -EINVAL;
// 找到协议族相关的策略信息结构
 afinfo = xfrm_policy_lock_afinfo(family);
 if (unlikely(afinfo == NULL))
  return -EAFNOSUPPORT;
err = -ENOENT;
// 策略信息结构中的模式数组
 modemap = afinfo->mode_map;
// 数组元素等于要拆除的模式, 清空, 返回成功
 if (likely(modemap[mode->encap] == mode)) {
  modemap[mode->encap] = NULL;
  err = 0;
 }
xfrm_policy_unlock_afinfo(afinfo);
 return err;
}
EXPORT_SYMBOL(xfrm_unregister_mode);

// 查找模式
struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
{
 struct xfrm_policy_afinfo *afinfo;
 struct xfrm_mode *mode;
 int modload_attempted = 0;
if (unlikely(encap >= XFRM_MODE_MAX))
  return NULL;
retry:
// 找到协议族相关的策略信息结构
 afinfo = xfrm_policy_get_afinfo(family);
 if (unlikely(afinfo == NULL))
  return NULL;
// 策略信息结构中的模式数组
 mode = afinfo->mode_map[encap];
// 增加模式模块的使用计数
 if (unlikely(mode && !try_module_get(mode->owner)))
  mode = NULL;
// 如果当前模式为空, 则加载模式对应的内核模块, 重新查找
 if (!mode && !modload_attempted) {
  xfrm_policy_put_afinfo(afinfo);
  request_module("xfrm-mode-%d-%d", family, encap);
  modload_attempted = 1;
  goto retry;
 }
xfrm_policy_put_afinfo(afinfo);
 return mode;
}

// 释放模式模块使用计数
void xfrm_put_mode(struct xfrm_mode *mode)
{
 module_put(mode->owner);
}

5.8.3 协议信息处理
// 登记协议信息结构
int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
{
 int err = 0;
 if (unlikely(afinfo == NULL))
  return -EINVAL;
 if (unlikely(afinfo->family >= NPROTO))
  return -EAFNOSUPPORT;
 write_lock_bh(&xfrm_policy_afinfo_lock);
// 数组中的对应协议的协议信息结构元素应该为空
 if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
  err = -ENOBUFS;
 else {
// 安全路由操作结构
  struct dst_ops *dst_ops = afinfo->dst_ops;
// 安全路由操作结构的参数和操作函数赋值
  if (likely(dst_ops->kmem_cachep == NULL))
   dst_ops->kmem_cachep = xfrm_dst_cache;
  if (likely(dst_ops->check == NULL))
   dst_ops->check = xfrm_dst_check;
  if (likely(dst_ops->negative_advice == NULL))
   dst_ops->negative_advice = xfrm_negative_advice;
  if (likely(dst_ops->link_failure == NULL))
   dst_ops->link_failure = xfrm_link_failure;
  if (likely(afinfo->garbage_collect == NULL))
   afinfo->garbage_collect = __xfrm_garbage_collect;
// 数组中的对应协议的协议信息结构元素填为协议信息结构
  xfrm_policy_afinfo[afinfo->family] = afinfo;
 }
 write_unlock_bh(&xfrm_policy_afinfo_lock);
 return err;
}
EXPORT_SYMBOL(xfrm_policy_register_afinfo);

// 拆除协议信息结构
int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
{
 int err = 0;
 if (unlikely(afinfo == NULL))
  return -EINVAL;
 if (unlikely(afinfo->family >= NPROTO))
  return -EAFNOSUPPORT;
 write_lock_bh(&xfrm_policy_afinfo_lock);
 if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) {
// 数组中的协议信息结构等于指定的信息结构
  if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo))
   err = -EINVAL;
  else {
// 清空协议信息数组元素和路由操作结构参数
   struct dst_ops *dst_ops = afinfo->dst_ops;
   xfrm_policy_afinfo[afinfo->family] = NULL;
   dst_ops->kmem_cachep = NULL;
   dst_ops->check = NULL;
   dst_ops->negative_advice = NULL;
   dst_ops->link_failure = NULL;
   afinfo->garbage_collect = NULL;
  }
 }
 write_unlock_bh(&xfrm_policy_afinfo_lock);
 return err;
}
EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);

// 查找协议信息结构, 加读锁
static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
{
 struct xfrm_policy_afinfo *afinfo;
 if (unlikely(family >= NPROTO))
  return NULL;
 read_lock(&xfrm_policy_afinfo_lock);
// 获取指定协议位置处的协议信息结构
 afinfo = xfrm_policy_afinfo[family];
// 如果该协议信息结构不存在, 解锁
 if (unlikely(!afinfo))
  read_unlock(&xfrm_policy_afinfo_lock);
 return afinfo;
}

// 释放协议信息结构, 解读锁
static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
{
 read_unlock(&xfrm_policy_afinfo_lock);
}

// 协议信息结构加写锁, 返回指定的协议信息结构, 错误时返回NULL
static struct xfrm_policy_afinfo *xfrm_policy_lock_afinfo(unsigned int family)
{
 struct xfrm_policy_afinfo *afinfo;
 if (unlikely(family >= NPROTO))
  return NULL;
 write_lock_bh(&xfrm_policy_afinfo_lock);
// 获取指定协议位置处的协议信息结构
 afinfo = xfrm_policy_afinfo[family];
// 如果该协议信息结构不存在, 解锁
 if (unlikely(!afinfo))
  write_unlock_bh(&xfrm_policy_afinfo_lock);
 return afinfo;
}
// 协议信息结构解写锁
static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo)
{
 write_unlock_bh(&xfrm_policy_afinfo_lock);
}
5.8.4 网卡回调

// 网卡通知结构
static struct notifier_block xfrm_dev_notifier = {
 xfrm_dev_event,
 NULL,
 0
};
// 回调函数
static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
 switch (event) {
// 就只响应网卡停事件, 删除和网卡相关的所有安全路由项
 case NETDEV_DOWN:
  xfrm_flush_bundles();
 }
 return NOTIFY_DONE;
}
static int xfrm_flush_bundles(void)
{
// 也是使用xfrm_prune_bundles()函数进行删除操作
// 条件函数是stale_bundle
 xfrm_prune_bundles(stale_bundle);
 return 0;
}
// 判断安全路由项是否可用
// 返回1表示不可用, 0表示可用
static int stale_bundle(struct dst_entry *dst)
{
 return !xfrm_bundle_ok(NULL, (struct xfrm_dst *)dst, NULL, AF_UNSPEC, 0);
}
// 返回0表示不可用, 1表示可用
int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
  struct flowi *fl, int family, int strict)
{
 struct dst_entry *dst = &first->u.dst;
 struct xfrm_dst *last;
 u32 mtu;
// 检查路由项
 if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) ||
// 检查网卡是否在运行
     (dst->dev && !netif_running(dst->dev)))
  return 0;
last = NULL;
do {
// 安全路由
  struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
// 检查SA选择子是否匹配流结构
  if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family))
   return 0;
  if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm, pol))
   return 0;
// 检查SA状态是否合法
  if (dst->xfrm->km.state != XFRM_STATE_VALID)
   return 0;
  if (xdst->genid != dst->xfrm->genid)
   return 0;
// 严格检查时, 检查非通道模式下的SA地址和流结构参数是否匹配
  if (strict && fl && dst->xfrm->props.mode != XFRM_MODE_TUNNEL &&
      !xfrm_state_addr_flow_check(dst->xfrm, fl, family))
   return 0;
// 子路由项的MTU
  mtu = dst_mtu(dst->child);
  if (xdst->child_mtu_cached != mtu) {
   last = xdst;
   xdst->child_mtu_cached = mtu;
  }
// 通用路由检查
  if (!dst_check(xdst->route, xdst->route_cookie))
   return 0;
// 安全路由相关的普通路由的MTU
  mtu = dst_mtu(xdst->route);
  if (xdst->route_mtu_cached != mtu) {
   last = xdst;
   xdst->route_mtu_cached = mtu;
  }
// 遍历安全路由链表
  dst = dst->child;
 } while (dst->xfrm);
// last是最后一个和子路由和普通路由的MTU不同的安全路由, 一般都是相同的
 if (likely(!last))
  return 1;
// 调整各路由项中的MTU
 mtu = last->child_mtu_cached;
 for (;;) {
  dst = &last->u.dst;
mtu = xfrm_state_mtu(dst->xfrm, mtu);
  if (mtu > last->route_mtu_cached)
   mtu = last->route_mtu_cached;
  dst->metrics[RTAX_MTU-1] = mtu;
if (last == first)
   break;
last = last->u.next;
  last->child_mtu_cached = mtu;
 }
return 1;
}

5.9 小结
xfrm_policy相关函数的调用被调用关系可如下简单表示:
ip_route_output_flow
  ->xfrm_lookup: find xfrm_dst for the skb, create dst_list
    -> xfrm_sk_policy_lookup
    -> flow_cache_lookup
    -> xfrm_find_bundle
    -> xfrm_policy_lookup_bytype
    -> xfrm_tmpl_resolve
      -> xfrm_tmpl_resolve_one
        -> xfrm_get_saddr
          -> afinfo->get_saddr == xfrm4_get_saddr
            -> xfrm4_dst_lookup
        -> xfrm_state_find
          -> __xfrm_state_lookup
          -> xfrm_state_alloc
          -> km_query
            -> km->acquire (pfkey_acquire, xfrm_send_acquire)
      -> xfrm_state_sort
        -> afinfo->state_sort == NULL
    -> km_wait_queue
    -> xfrm_bundle_create

do_ip_setsockopt
  -> xfrm_user_policy
    -> km->compile_policy
 -> xfrm_sk_policy_insert
pfkey_compile_policy
  -> xfrm_policy_alloc
    timer.func=xfrm_policy_timer

pfkey_spdadd
  -> xfrm_policy_alloc
  -> xfrm_policy_insert
    -> policy_hash_bysel
    -> selector_cmp
    -> xfrm_sel_ctx_match
   
pfkey_spddelete
  -> xfrm_policy_bysel_ctx
    -> policy_hash_bysel
    -> xfrm_sel_ctx_match
   
pfkey_spdget
  -> xfrm_policy_byid

xfrm_flush_policy
pfkey_policy_flush
  -> xfrm_policy_flush
    -> xfrm_policy_kill

xfrm_dump_policy
  -> xfrm_policy_walk
    -> dump_one_policy
pfkey_spddump
  -> xfrm_policy_walk
    -> dump_sp
gen_reqid
  -> xfrm_policy_walk
    -> check_reqid

xfrm_add_pol_expire
xfrm_policy_timer
  -> xfrm_policy_delete
    -> __xfrm_policy_unlink
    -> xfrm_policy_kill

xfrm_sk_policy_insert
  -> xfrm_get_index
  -> __xfrm_policy_link
  -> __xfrm_policy_unlink
  -> xfrm_policy_kill
 

xfrm_sk_clone_policy
  -> __xfrm_sk_clone_policy
    -> clone_policy
      -> xfrm_policy_alloc
      -> __xfrm_policy_link

xfrm_decode_session
  -> xfrm4_decode_session

xfrm4_route_forward
  -> xfrm_route_forward
    -> __xfrm_route_forward
      -> xfrm4_decode_session
      -> xfrm_lookup

xfrm4_policy_check
  -> xfrm_policy_check
    -> __xfrm_policy_check
      -> xfrm4_decode_session
      -> __xfrm_sk_policy_lookup
        -> xfrm_selector_match
      -> __flow_cache_lookup
        -> xfrm_policy_lookup
        -> xfrm_policy_lookup_bytype
          -> policy_hash_direct
          -> xfrm_policy_match
            -> xfrm_selector_match
      -> xfrm_policy_lookup_bytype
      -> xfrm_tmpl_sort
      -> xfrm_policy_ok
        -> xfrm_state_ok
       
xfrm_flush_bundles
  -> xfrm_prune_bundles
    -> prune_one_bundles
      -> stale_bundle 
   

Linux内核中的IPSEC实现(3)相关推荐

  1. Linux内核中的IPSEC实现(1)

    本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途. msn: yfydz_no1@hotmail.com 来源:http:/ ...

  2. Linux内核中的IPSEC实现(7)

    本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途. msn: yfydz_no1@hotmail.com 来源:http:/ ...

  3. Linux内核中的IPSEC实现2

    http://blog.chinaunix.net/uid-127037-id-2919563.html 5. 安全策略(xfrm_policy)处理 本节所介绍的函数都在net/xfrm/xfrm_ ...

  4. Linux内核中的IPSEC实现(3) ---转载

    本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途. msn: yfydz_no1@hotmail.com 来源:http:/ ...

  5. linux内核中TCP接收的实现

    linux内核中TCP接收的实现入口函数是tcp_v4_rcv 1. 数据包检查处理 一开始做一些数据包详细检查处理,一旦出错,可能导致内核挂掉 int tcp_v4_rcv(struct sk_bu ...

  6. Linux内核中锁机制之完成量、互斥量

    在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...

  7. 简单谈一点linux内核中套接字的bind机制--数据结构以及端口确定

    众所周知,创建一个套接字可以bind到一个特定的ip地址和端口,实际上套接字这一概念代表了TCP/IP协议栈的应用层标识,协议栈中的应用层就是通过一个ip地址和一个端口号标识的,当然这仅仅是对于TCP ...

  8. Linux 内核中的 Device Mapper 机制

    本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...

  9. 如何放出Linux内核中的链表大招

    前言 上回,我们说到Linux内核中max()宏的终极奥义,Linux内核链表也不甘示弱,那么接下来,让我们看看Linux内核中的链表大招. 如何放出Linux内核中的链表大招 前言 一.链表简介 ( ...

最新文章

  1. 《强化学习周刊》第40期:PMIC多智能体强化学习、Lazy-MDPs、CTDS
  2. 【听课笔记】国立交通大学《如何成功投稿国际科学期刊》 投稿准备+审稿流程剖析...
  3. python多线程爬虫实例-Python实现多线程爬虫
  4. Maven入门极简使用教程
  5. 大一计算机上机试题2017,2017历年全国计算机二级ACCESS上机试题及答案
  6. c++局部对象是什么_程序员每日一题-GCROOT对象
  7. 将React Native升级到最新版本的最简单方法
  8. Python离线环境
  9. 从一个表复制到另一个表SQL
  10. blog.mm index.php,每天一个WordPress文件:index.php
  11. 【转】Linux里如何查找文件内容
  12. ArrayList源码解析(基于Java8)
  13. Dijkstra算法图解
  14. s7200cpu224xp手册_西门子plc s7-200系列|s7-200 cpu224xp|CPU224 XP 高速I/O
  15. IC卡防复制 设备联网 动态密钥方案说明 一卡通 门禁卡防破解Mifare卡低成本动态加密实现思路
  16. 大平原顾问快讯FRX移至新服务器
  17. 塔防类游戏实现(一)
  18. vc2005和IE冲突 主要是在通过向导添加方法或变量的时候会报脚本错误。
  19. TestCenter测试管理工具功能详解二(G)
  20. Flex弹性盒子的项目属性

热门文章

  1. 登录注册页面,JS判断用户手机号码是否已经存在,或者格式不正确
  2. 随机变量序列的两种收敛性
  3. 跨境物流的难题,亚马逊官方跨境物流公司,跨境物流海外仓
  4. label标签中for的使用
  5. React 中闭包陷阱问题分析
  6. 路由器与交换机的工作原理(转)
  7. 想问一下杭州恒生电子证券事业部如何
  8. 2022年上海市安全员C证最新解析及上海市安全员C证考试技巧
  9. fwrite函数与fflush函数
  10. 22考研经验贴-这是可以说的吗?