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

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

9. IPSEC封装流程
 
IPSEC
数据包的封装过程是在数据包发出前完成的, 是和路由选择密切相关的, 根据前面的发出分析可知封装是通过对数据设置安全路由链表来实现的, 因此对数据包的IPSEC封装流程可以简单描述如下:
1) 对于进入的数据包, 进行路由选择, 如果是转发的, 进入路由输入, 然后查找安全策略检查是否需要IPSEC封装, 如果需要封装, 就查找和创建相关的安全路由, 进入路由输出处理, 在路由输出时即按照安全路由一层层地封装数据包最后得到IPSEC包发出;
2) 对于自身发出的数据包, 需要进行路由选择, 选定路由后进入路由输入, 查找安全策略进行处理, 以后和转发的数据包IPSEC封装就是完全相同了。

9.1 转发包的封装

数据的转发入口点函数是ip_forward, 进入该函数的数据包还是普通数据包,数据包的路由也是普通路由:

/* net/ipv4/ip_forward.c */
int ip_forward(struct sk_buff *skb)
{
 struct iphdr *iph; /* Our header */
 struct rtable *rt; /* Route we use */
 struct ip_options * opt = &(IPCB(skb)->opt);
// 对转发的数据包进行安全策略检查, 检查失败的话丢包
 if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
  goto drop;
 if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
  return NET_RX_SUCCESS;
// 转发包也是到自身的包, 不是的话丢包
 if (skb->pkt_type != PACKET_HOST)
  goto drop;
 skb->ip_summed = CHECKSUM_NONE;
 
 /*
  * According to the RFC, we must first decrease the TTL field. If
  * that reaches zero, we must reply an ICMP control message telling
  * that the packet's lifetime expired.
  */
// TTL到头了, 丢包
 if (skb->nh.iph->ttl <= 1)
                goto too_many_hops;
// 进入安全路由选路和转发处理, 在此函数中构造数据包的安全路由
 if (!xfrm4_route_forward(skb))
  goto drop;
// 以下是一些常规的路由和TTL处理
 rt = (struct rtable*)skb->dst;
 if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
  goto sr_failed;
 /* We are about to mangle packet. Copy it! */
 if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))
  goto drop;
 iph = skb->nh.iph;
 /* Decrease ttl after skb cow done */
 ip_decrease_ttl(iph);
 /*
  * We now generate an ICMP HOST REDIRECT giving the route
  * we calculated.
  */
 if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr)
  ip_rt_send_redirect(skb);
 skb->priority = rt_tos2priority(iph->tos);
// 进行FORWARD点过滤, 过滤后进入ip_forward_finish函数
 return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,
         ip_forward_finish);
sr_failed:
        /*
  * Strict routing permits no gatewaying
  */
         icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
         goto drop;
too_many_hops:
        /* Tell the sender its packet died... */
        IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
        icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:
 kfree_skb(skb);
 return NET_RX_DROP;
}
// ip_forward_finish函数主要就是调用dst_output函数
static inline int ip_forward_finish(struct sk_buff *skb)
{
 struct ip_options * opt = &(IPCB(skb)->opt);
 IP_INC_STATS_BH(IPSTATS_MIB_OUTFORWDATAGRAMS);
 if (unlikely(opt->optlen))
  ip_forward_options(skb);
 
 return dst_output(skb);
}

核心函数是xfrm4_route_forward函数
/* include/net/xfrm.h */
static inline int xfrm4_route_forward(struct sk_buff *skb)
{
 return xfrm_route_forward(skb, AF_INET);
}

static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{
// 如果没有发出方向的安全策略的话返回
 return !xfrm_policy_count[XFRM_POLICY_OUT] ||
// 如果路由标志专门设置不进行IPSEC封装的话也返回
  (skb->dst->flags & DST_NOXFRM) ||
  __xfrm_route_forward(skb, family);
}
/* net/xfrm/xfrm_policy.c */
int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{
 struct flowi fl;
// 路由解码, 填充流结构参数,
// 对IPV4实际调用的是_decode_session4(net/ipv4/xfrm4_policy.c)函数
 if (xfrm_decode_session(skb, &fl, family) < 0)
  return 0;
// 根据流结构查找安全路由, 没找到的话创建新的安全路由, 最后形成安全路由链表
// 见前几节中的分析
 return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0;
}

因此数据进行转发处理后, 最终进入dst_output函数处理
  

转发函数流程小结:
  
ip_forward
  -> xfrm4_route_forward (net/xfrm.h, get xfrm_dst)
    -> xfrm_route_forward
      -> __xfrm_route_forward
        -> xfrm_lookup
          -> xfrm_find_bundle
            -> afinfo->find_bundle == __xfrm4_find_bundle
          -> xfrm_bundle_create
            -> afinfo->bundle_cr