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

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

在xfrm中各种和地址相关的操作是和协议族相关的, 因此这部分的具体实现就放在相关的协议族实现中, 然后通过状态和策略信息结构来指引到实际的操作中,完成对普通数据包的IPSEC包装或对IPSEC包的解封装。

7.1 IPV4下的xfrm策略

IPV4下的xfrm策略在net/ipv4/xfrm4_policy.c文件中定义, 主要是定义IPV4的策略信息结构:

static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
 .family =   AF_INET,
 .dst_ops =  &xfrm4_dst_ops,
 .dst_lookup =  xfrm4_dst_lookup,
 .get_saddr =  xfrm4_get_saddr,
 .find_bundle =   __xfrm4_find_bundle,
 .bundle_create = __xfrm4_bundle_create,
 .decode_session = _decode_session4,
};

在xfrm_policy_register_afinfo()函数中, 还定义了struct xfrm_policy_afinfo结构的其他几个成员函数,因为这几个函数是和协议无关的, 所以在登记函数中定义了:
 afinfo->garbage_collect = __xfrm_garbage_collect;
该函数已经在本系列的第3篇中介绍过了.
 
以下是结构中几个函数的定义:
// IPV4的路由查找, 就是普通是路由查找方法
// 返回0成功
static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
{
 return __ip_route_output_key((struct rtable**)dst, fl);
}
// 查找地址, 这个函数是在通道模式下, 源地址没明确指定时调用的,查找获取
// 外部头中的源地址
static int xfrm4_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
{
 struct rtable *rt;
// 通道的流结构定义,用于查找路由
 struct flowi fl_tunnel = {
  .nl_u = {
   .ip4_u = {
    .daddr = daddr->a4,
   },
  },
 };
// 根据目的地址找路由
 if (!xfrm4_dst_lookup((struct xfrm_dst **)&rt, &fl_tunnel)) {
// 将找到的路由项中的源地址作为通道模式下的外部源地址
  saddr->a4 = rt->rt_src;
  dst_release(&rt->u.dst);
  return 0;
 }
 return -EHOSTUNREACH;
}

// 查找策略中的安全路由, 查找条件是流结构的定义的参数
static struct dst_entry *
__xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
{
 struct dst_entry *dst;
 read_lock_bh(&policy->lock);
// 遍历策略的安全路由链表
 for (dst = policy->bundles; dst; dst = dst->next) {
  struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
// 比较网卡位置, 目的地址, 源地址, TOS值是否匹配
// 同时检查该安全路由是否可用
  if (xdst->u.rt.fl.oif == fl->oif && /*XXX*/
      xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
          xdst->u.rt.fl.fl4_src == fl->fl4_src &&
          xdst->u.rt.fl.fl4_tos == fl->fl4_tos &&
      xfrm_bundle_ok(policy, xdst, fl, AF_INET, 0)) {
   dst_clone(dst);
   break;
  }
 }
 read_unlock_bh(&policy->lock);
 return dst;
}

// 解码skb数据, 填充流结构
static void
_decode_session4(struct sk_buff *skb, struct flowi *fl)
{
 struct iphdr *iph = skb->nh.iph;
// xprth是IP头后的上层协议头起始
 u8 *xprth = skb->nh.raw + iph->ihl*4;
// 先将流结构清零
 memset(fl, 0, sizeof(struct flowi));
// 数据包必须不是分片包
 if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
  switch (iph->protocol) {
// 对UDP(17), TCP(6), SCTP(132)和DCCP(33)协议, 要提取源端口和目的端口
// 头4字节是源端口和目的端口
  case IPPROTO_UDP:
  case IPPROTO_TCP:
  case IPPROTO_SCTP:
  case IPPROTO_DCCP:
// 要让skb预留出IP头长度加4字节的长度, 在IP层data应该指向最外面的IP头
   if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
    u16 *ports = (u16 *)xprth;
// 提取端口参数
    fl->fl_ip_sport = ports[0];
    fl->fl_ip_dport = ports[1];
   }
   break;
  case IPPROTO_ICMP:
// 对ICMP(1)协议要提取ICMP包的类型和编码, 2字节
   if (pskb_may_pull(skb, xprth + 2 - skb->data)) {
    u8 *icmp = xprth;
    fl->fl_icmp_type = icmp[0];
    fl->fl_icmp_code = icmp[1];
   }
   break;
  case IPPROTO_ESP:
// 对于ESP(50)协议要提取其中的SPI值, 4字节
   if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
    __be32 *ehdr = (__be32 *)xprth;
    fl->fl_ipsec_spi = ehdr[0];
   }
   break;
  case IPPROTO_AH:
// 对于AH(51)协议要提取其中的SPI值, 4字节
   if (pskb_may_pull(skb, xprth + 8 - skb->data)) {
    __be32 *ah_hdr = (__be32*)xprth;
    fl->fl_ipsec_spi = ah_hdr[1];
   }
   break;
  case IPPROTO_COMP:
// 对于COMP(108)协议要提取其中CPI值作为SPI值, 2字节
   if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
    __be16 *ipcomp_hdr = (__be16 *)xprth;
    fl->fl_ipsec_spi = htonl(ntohs(ipcomp_hdr[1]));
   }
   break;
  default:
   fl->fl_ipsec_spi = 0;
   break;
  };
 }
// 填充协议,源地址,目的地址, TOS参数
 fl->proto = iph->protocol;
 fl->fl4_dst = iph->daddr;
 fl->fl4_src =