quhr 发表于 2011-12-21 08:43

Linux内核中的IPSEC实现(5)

<table width="760" align="center" bgcolor="#ffffff" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td align="center"><table style="border-collapse: collapse; word-wrap: break-word;" width="740" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td width="740"><div id="art" width="560" style="margin: 15px;">

<div align="center"><font size="5">Linux内核中的IPSEC实现(5)</font></div>
<div><br>本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。<br>msn: <a href="mailto:yfydz_no1@hotmail.com" target="_blank">yfydz_no1@hotmail.com</a><br>来源:<a href="http://yfydz.cublog.cn/" target="_blank">http://yfydz.cublog.cn</a></div>
<div><br>7. IPV4下的xfrm支持处理</div>
<div><br>在xfrm中各种和地址相关的操作是和协议族相关的, 因此这部分的具体实现就放在相关的协议族实现中, 然后通过状态和策略信息结构来指引到实际的操作中,完成对普通数据包的IPSEC包装或对IPSEC包的解封装。</div>
<div><br>7.1 IPV4下的xfrm策略</div>
<div><br>IPV4下的xfrm策略在net/ipv4/xfrm4_policy.c文件中定义, 主要是定义IPV4的策略信息结构:</div>
<div><br>static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {<br>&nbsp;.family = &nbsp;&nbsp;AF_INET,<br>&nbsp;.dst_ops =&nbsp;&nbsp;&amp;xfrm4_dst_ops,<br>&nbsp;.dst_lookup =&nbsp;&nbsp;xfrm4_dst_lookup,<br>&nbsp;.get_saddr =&nbsp;&nbsp;xfrm4_get_saddr,<br>&nbsp;.find_bundle = &nbsp;&nbsp;__xfrm4_find_bundle,<br>&nbsp;.bundle_create =&nbsp;__xfrm4_bundle_create,<br>&nbsp;.decode_session =&nbsp;_decode_session4,<br>};</div>
<div><br>在xfrm_policy_register_afinfo()函数中, 还定义了struct xfrm_policy_afinfo结构的其他几个成员函数,因为这几个函数是和协议无关的, 所以在登记函数中定义了:<br>&nbsp;afinfo-&gt;garbage_collect = __xfrm_garbage_collect;<br>该函数已经在本系列的第3篇中介绍过了.</div>
<div>&nbsp;</div>
<div>以下是结构中几个函数的定义:</div>
<div>// IPV4的路由查找, 就是普通是路由查找方法<br>// 返回0成功<br>static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)<br>{<br>&nbsp;return __ip_route_output_key((struct rtable**)dst, fl);<br>}</div>
<div>// 查找地址, 这个函数是在通道模式下, 源地址没明确指定时调用的,查找获取<br>// 外部头中的源地址<br>static int xfrm4_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)<br>{<br>&nbsp;struct rtable *rt;<br>// 通道的流结构定义,用于查找路由<br>&nbsp;struct flowi fl_tunnel = {<br>&nbsp;&nbsp;.nl_u = {<br>&nbsp;&nbsp;&nbsp;.ip4_u = {<br>&nbsp;&nbsp;&nbsp;&nbsp;.daddr = daddr-&gt;a4,<br>&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;},<br>&nbsp;};</div>
<div>// 根据目的地址找路由<br>&nbsp;if (!xfrm4_dst_lookup((struct xfrm_dst **)&amp;rt, &amp;fl_tunnel)) {<br>// 将找到的路由项中的源地址作为通道模式下的外部源地址<br>&nbsp;&nbsp;saddr-&gt;a4 = rt-&gt;rt_src;<br>&nbsp;&nbsp;dst_release(&amp;rt-&gt;u.dst);<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>&nbsp;return -EHOSTUNREACH;<br>}</div>
<div><br>// 查找策略中的安全路由, 查找条件是流结构的定义的参数<br>static struct dst_entry *<br>__xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy)<br>{<br>&nbsp;struct dst_entry *dst;</div>
<div>&nbsp;read_lock_bh(&amp;policy-&gt;lock);<br>// 遍历策略的安全路由链表<br>&nbsp;for (dst = policy-&gt;bundles; dst; dst = dst-&gt;next) {<br>&nbsp;&nbsp;struct xfrm_dst *xdst = (struct xfrm_dst*)dst;<br>// 比较网卡位置, 目的地址, 源地址, TOS值是否匹配<br>// 同时检查该安全路由是否可用<br>&nbsp;&nbsp;if (xdst-&gt;u.rt.fl.oif == fl-&gt;oif &amp;&amp;&nbsp;/*XXX*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xdst-&gt;u.rt.fl.fl4_dst == fl-&gt;fl4_dst &amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; xdst-&gt;u.rt.fl.fl4_src == fl-&gt;fl4_src &amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; xdst-&gt;u.rt.fl.fl4_tos == fl-&gt;fl4_tos &amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xfrm_bundle_ok(policy, xdst, fl, AF_INET, 0)) {<br>&nbsp;&nbsp;&nbsp;dst_clone(dst);<br>&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;read_unlock_bh(&amp;policy-&gt;lock);<br>&nbsp;return dst;<br>}</div>
<div><br>// 解码skb数据, 填充流结构<br>static void<br>_decode_session4(struct sk_buff *skb, struct flowi *fl)<br>{<br>&nbsp;struct iphdr *iph = skb-&gt;nh.iph;<br>// xprth是IP头后的上层协议头起始<br>&nbsp;u8 *xprth = skb-&gt;nh.raw + iph-&gt;ihl*4;<br>// 先将流结构清零<br>&nbsp;memset(fl, 0, sizeof(struct flowi));<br>// 数据包必须不是分片包<br>&nbsp;if (!(iph-&gt;frag_off &amp; htons(IP_MF | IP_OFFSET))) {<br>&nbsp;&nbsp;switch (iph-&gt;protocol) {<br>// 对UDP(17), TCP(6), SCTP(132)和DCCP(33)协议, 要提取源端口和目的端口<br>// 头4字节是源端口和目的端口<br>&nbsp;&nbsp;case IPPROTO_UDP:<br>&nbsp;&nbsp;case IPPROTO_TCP:<br>&nbsp;&nbsp;case IPPROTO_SCTP:<br>&nbsp;&nbsp;case IPPROTO_DCCP:<br>// 要让skb预留出IP头长度加4字节的长度, 在IP层data应该指向最外面的IP头<br>&nbsp;&nbsp;&nbsp;if (pskb_may_pull(skb, xprth + 4 - skb-&gt;data)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;u16 *ports = (u16 *)xprth;<br>// 提取端口参数<br>&nbsp;&nbsp;&nbsp;&nbsp;fl-&gt;fl_ip_sport = ports;<br>&nbsp;&nbsp;&nbsp;&nbsp;fl-&gt;fl_ip_dport = ports;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;break;</div>
<div>&nbsp;&nbsp;case IPPROTO_ICMP:<br>// 对ICMP(1)协议要提取ICMP包的类型和编码, 2字节<br>&nbsp;&nbsp;&nbsp;if (pskb_may_pull(skb, xprth + 2 - skb-&gt;data)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;u8 *icmp = xprth;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;fl-&gt;fl_icmp_type = icmp;<br>&nbsp;&nbsp;&nbsp;&nbsp;fl-&gt;fl_icmp_code = icmp;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;break;</div>
<div>&nbsp;&nbsp;case IPPROTO_ESP:<br>// 对于ESP(50)协议要提取其中的SPI值, 4字节<br>&nbsp;&nbsp;&nbsp;if (pskb_may_pull(skb, xprth + 4 - skb-&gt;data)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;__be32 *ehdr = (__be32 *)xprth;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;fl-&gt;fl_ipsec_spi = ehdr;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;break;</div>
<div>&nbsp;&nbsp;case IPPROTO_AH:<br>// 对于AH(51)协议要提取其中的SPI值, 4字节<br>&nbsp;&nbsp;&nbsp;if (pskb_may_pull(skb, xprth + 8 - skb-&gt;data)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;__be32 *ah_hdr = (__be32*)xprth;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;fl-&gt;fl_ipsec_spi = ah_hdr;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;break;</div>
<div>&nbsp;&nbsp;case IPPROTO_COMP:<br>// 对于COMP(108)协议要提取其中CPI值作为SPI值, 2字节<br>&nbsp;&nbsp;&nbsp;if (pskb_may_pull(skb, xprth + 4 - skb-&gt;data)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;__be16 *ipcomp_hdr = (__be16 *)xprth;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;fl-&gt;fl_ipsec_spi = htonl(ntohs(ipcomp_hdr));<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;default:<br>&nbsp;&nbsp;&nbsp;fl-&gt;fl_ipsec_spi = 0;<br>&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;};<br>&nbsp;}<br>// 填充协议,源地址,目的地址, TOS参数<br>&nbsp;fl-&gt;proto = iph-&gt;protocol;<br>&nbsp;fl-&gt;fl4_dst = iph-&gt;daddr;<br>&nbsp;fl-&gt;fl4_src = iph-&gt;saddr;<br>&nbsp;fl-&gt;fl4_tos = iph-&gt;tos;<br>}</div>
<div>&nbsp;</div>
<div><br>/* Allocate chain of dst_entry's, attach known xfrm's, calculate<br>&nbsp;* all the metrics... Shortly, bundle a bundle.<br>&nbsp;*/<br>// 创建安全路由<br>static int<br>__xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct flowi *fl, struct dst_entry **dst_p)<br>{<br>&nbsp;struct dst_entry *dst, *dst_prev;<br>&nbsp;struct rtable *rt0 = (struct rtable*)(*dst_p);<br>&nbsp;struct rtable *rt = rt0;<br>&nbsp;u32 remote = fl-&gt;fl4_dst;<br>&nbsp;u32 local&nbsp; = fl-&gt;fl4_src;<br>&nbsp;struct flowi fl_tunnel = {<br>&nbsp;&nbsp;.nl_u = {<br>&nbsp;&nbsp;&nbsp;.ip4_u = {<br>&nbsp;&nbsp;&nbsp;&nbsp;.saddr = local,<br>&nbsp;&nbsp;&nbsp;&nbsp;.daddr = remote,<br>&nbsp;&nbsp;&nbsp;&nbsp;.tos = fl-&gt;fl4_tos<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;};<br>&nbsp;int i;<br>&nbsp;int err;<br>&nbsp;int header_len = 0;<br>&nbsp;int trailer_len = 0;</div>
<div>&nbsp;dst = dst_prev = NULL;<br>&nbsp;dst_hold(&amp;rt-&gt;u.dst);</div>
<div>// 循环次数为策略中SA的数量, 每个SA对应一个安全路由, 一个安全路由对应对数据包的一个<br>// 操作: 如压缩, ESP封装, AH封装等<br>&nbsp;for (i = 0; i &lt; nx; i++) {<br>// 分配安全路由, 安全路由的操作结构是xfrm4_dst_ops<br>// 因为定义了很多不同类型的路由, 每种路由都有各自的操作结构, 这样在上层可用<br>// 统一的接口进行路由处理<br>&nbsp;&nbsp;struct dst_entry *dst1 = dst_alloc(&amp;xfrm4_dst_ops);<br>&nbsp;&nbsp;struct xfrm_dst *xdst;<br>&nbsp;&nbsp;int tunnel = 0;</div>
<div>&nbsp;&nbsp;if (unlikely(dst1 == NULL)) {<br>&nbsp;&nbsp;&nbsp;err = -ENOBUFS;<br>&nbsp;&nbsp;&nbsp;dst_release(&amp;rt-&gt;u.dst);<br>&nbsp;&nbsp;&nbsp;goto error;<br>&nbsp;&nbsp;}</div>
<div>&nbsp;&nbsp;if (!dst)<br>// 第一次循环<br>&nbsp;&nbsp;&nbsp;dst = dst1;<br>&nbsp;&nbsp;else {<br>// 将新分配的安全路由作为前一个路由的child<br>&nbsp;&nbsp;&nbsp;dst_prev-&gt;child = dst1;<br>&nbsp;&nbsp;&nbsp;dst1-&gt;flags |= DST_NOHASH;<br>&nbsp;&nbsp;&nbsp;dst_clone(dst1);<br>&nbsp;&nbsp;}</div>
<div>&nbsp;&nbsp;xdst = (struct xfrm_dst *)dst1;<br>// 安全路由中保留相应的普通路由<br>&nbsp;&nbsp;xdst-&gt;route = &amp;rt-&gt;u.dst;<br>&nbsp;&nbsp;xdst-&gt;genid = xfrm-&gt;genid;<br>// 新节点的next是老节点<br>&nbsp;&nbsp;dst1-&gt;next = dst_prev;<br>// 现在prev节点位新节点<br>&nbsp;&nbsp;dst_prev = dst1;<br>&nbsp;&nbsp;if (xfrm-&gt;props.mode != XFRM_MODE_TRANSPORT) {<br>&nbsp;&nbsp;&nbsp;remote = xfrm-&gt;id.daddr.a4;<br>&nbsp;&nbsp;&nbsp;local&nbsp; = xfrm-&gt;props.saddr.a4;<br>&nbsp;&nbsp;&nbsp;tunnel = 1;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;header_len += xfrm-&gt;props.header_len;<br>&nbsp;&nbsp;trailer_len += xfrm-&gt;props.trailer_len;</div>
<div>// 如果是通道模式, 需要重新包裹外部IP头, 需要重新寻找外部IP头的路由<br>&nbsp;&nbsp;if (tunnel) {<br>&nbsp;&nbsp;&nbsp;fl_tunnel.fl4_src = local;<br>&nbsp;&nbsp;&nbsp;fl_tunnel.fl4_dst = remote;<br>&nbsp;&nbsp;&nbsp;err = xfrm_dst_lookup((struct xfrm_dst **)&amp;rt,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;fl_tunnel, AF_INET);<br>&nbsp;&nbsp;&nbsp;if (err)<br>&nbsp;&nbsp;&nbsp;&nbsp;goto error;<br>&nbsp;&nbsp;} else<br>&nbsp;&nbsp;&nbsp;dst_hold(&amp;rt-&gt;u.dst);<br>&nbsp;}<br>// 将最新节点的child指向最后的普通路由<br>&nbsp;dst_prev-&gt;child = &amp;rt-&gt;u.dst;<br>// 最老一个安全路由的path指向最后的普通路由<br>&nbsp;dst-&gt;path = &amp;rt-&gt;u.dst;</div>
<div>// 将最老安全路由点作为要返回的路由节点链表头<br>&nbsp;*dst_p = dst;<br>// dst现在是最新节点<br>&nbsp;dst = dst_prev;<br>// prev现在指向最老安全节点<br>&nbsp;dst_prev = *dst_p;<br>&nbsp;i = 0;</div>
<div>/*<br>&nbsp;为更好理解上面的操作, 用图来表示. 以上循环形成了下图水平方向的一个链表,
链表中的最左边的路由项节点dst为最老的安全路由项, 新分配的安全路由项通过child链接成链表, child通过next指向老节点,
最后一项是数据包封装完后的最后普通路由项.</div>
<div>垂直方向的链表是在xfrm_lookup()中形成的, 是多个策略同时起作用的情况, 一般情况下就只有一个策略, 本文中可不考虑多策略的情况.</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rt0.u.dst&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rt.u.dst&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rt.u.dst<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; route |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; route |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; route |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp; child&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp; child&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bundle&nbsp; +-----+&nbsp; -----&gt; +-----+ -----&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +-----+ child<br>&nbsp; policy -------&gt; | dst |&nbsp; &lt;----- | dst | &lt;----- ...&nbsp; | dst | -----&gt; rt.u.dst<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +-----+&nbsp;&nbsp; next&nbsp; +-----+&nbsp; next&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +-----+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |next<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; V&nbsp;&nbsp;&nbsp;&nbsp; child&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; child<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +-----+&nbsp; -----&gt; +-----+ -----&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +-----+ child<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | dst |&nbsp; &lt;----- | dst | &lt;----- ...&nbsp; | dst | -----&gt; rt.u.dst<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +-----+&nbsp;&nbsp; next&nbsp; +-----+&nbsp; next&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +-----+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |next<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; V<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....<br>*/</div>
<div>// 对新生成的每个安全路由项填充结构参数<br>&nbsp;for (; dst_prev != &amp;rt-&gt;u.dst; dst_prev = dst_prev-&gt;child) {<br>&nbsp;&nbsp;struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;<br>&nbsp;&nbsp;x-&gt;u.rt.fl = *fl;</div>
<div>&nbsp;&nbsp;dst_prev-&gt;xfrm = xfrm;<br>&nbsp;&nbsp;dst_prev-&gt;dev = rt-&gt;u.dst.dev;<br>&nbsp;&nbsp;if (rt-&gt;u.dst.dev)<br>&nbsp;&nbsp;&nbsp;dev_hold(rt-&gt;u.dst.dev);<br>&nbsp;&nbsp;dst_prev-&gt;obsolete&nbsp;= -1;<br>&nbsp;&nbsp;dst_prev-&gt;flags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |= DST_HOST;<br>&nbsp;&nbsp;dst_prev-&gt;lastuse&nbsp;= jiffies;<br>&nbsp;&nbsp;dst_prev-&gt;header_len&nbsp;= header_len;<br>&nbsp;&nbsp;dst_prev-&gt;nfheader_len&nbsp;= 0;<br>&nbsp;&nbsp;dst_prev-&gt;trailer_len&nbsp;= trailer_len;<br>&nbsp;&nbsp;memcpy(&amp;dst_prev-&gt;metrics, &amp;x-&gt;route-&gt;metrics, sizeof(dst_prev-&gt;metrics));</div>
<div>&nbsp;&nbsp;/* Copy neighbout for reachability confirmation */<br>&nbsp;&nbsp;dst_prev-&gt;neighbour&nbsp;= neigh_clone(rt-&gt;u.dst.neighbour);<br>&nbsp;&nbsp;dst_prev-&gt;input&nbsp;&nbsp;= rt-&gt;u.dst.input;<br>// 注意安全路由的输出函数是xfrm4_output, 在以后分析路由过程时要用到<br>&nbsp;&nbsp;dst_prev-&gt;output&nbsp;= xfrm4_output;<br>&nbsp;&nbsp;if (rt-&gt;peer)<br>&nbsp;&nbsp;&nbsp;atomic_inc(&amp;rt-&gt;peer-&gt;refcnt);<br>&nbsp;&nbsp;x-&gt;u.rt.peer = rt-&gt;peer;<br>&nbsp;&nbsp;/* Sheit... I remember I did this right. Apparently,<br>&nbsp;&nbsp; * it was magically lost, so this code needs audit */<br>&nbsp;&nbsp;x-&gt;u.rt.rt_flags = rt0-&gt;rt_flags&amp;(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);<br>&nbsp;&nbsp;x-&gt;u.rt.rt_type = rt-&gt;rt_type;<br>&nbsp;&nbsp;x-&gt;u.rt.rt_src = rt0-&gt;rt_src;<br>&nbsp;&nbsp;x-&gt;u.rt.rt_dst = rt0-&gt;rt_dst;<br>&nbsp;&nbsp;x-&gt;u.rt.rt_gateway = rt-&gt;rt_gateway;<br>&nbsp;&nbsp;x-&gt;u.rt.rt_spec_dst = rt0-&gt;rt_spec_dst;<br>&nbsp;&nbsp;x-&gt;u.rt.idev = rt0-&gt;idev;<br>&nbsp;&nbsp;in_dev_hold(rt0-&gt;idev);<br>&nbsp;&nbsp;header_len -= x-&gt;u.dst.xfrm-&gt;props.header_len;<br>&nbsp;&nbsp;trailer_len -= x-&gt;u.dst.xfrm-&gt;props.trailer_len;<br>&nbsp;}<br>// 初始化路由项的MTU值<br>&nbsp;xfrm_init_pmtu(dst);<br>&nbsp;return 0;</div>
<div>error:<br>&nbsp;if (dst)<br>&nbsp;&nbsp;dst_free(dst);<br>&nbsp;return err;<br>}</div>
<div>&nbsp;</div>
<div>7.1. 小结</div>
<div><br>IPV4的策略信息结构中的相关成员函数的被调用关系可如下简单表示:</div>
<div>xfrm_lookup: find xfrm_dst for the skb, create dst_list<br>&nbsp; -&gt; xfrm_find_bundle<br>&nbsp;&nbsp;&nbsp; -&gt; afinfo-&gt;find_bundle() == __xfrm4_find_bundle<br>&nbsp; -&gt; xfrm_tmpl_resolve<br>&nbsp;&nbsp;&nbsp; -&gt; xfrm_tmpl_resolve_one<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm_get_saddr<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; afinfo-&gt;get_saddr == xfrm4_get_saddr<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm4_dst_lookup<br>&nbsp; -&gt; xfrm_bundle_create<br>&nbsp;&nbsp;&nbsp; -&gt; afinfo-&gt;bundle_create() == __xfrm4_bundle_create<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm_dst_lookup()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; afinfo-&gt;dst_lookup() == xfrm4_dst_lookup</div>
<div><br>xfrm4_policy_check<br>&nbsp; -&gt; xfrm_policy_check<br>&nbsp;&nbsp;&nbsp; -&gt; __xfrm_policy_check<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm_decode_session<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; afinfo-&gt;decode_session() == _decode_session4</div>
<div><br>7.2 IPV4安全路由操作</div>
<div><br>路由操作是针对每种类型的路由定义的一个操作结构, 对上层隐藏了不同路由处理内部的处理方法, 对于IPSEC的IPV4安全路由(xfrm_dst)的操作结构定义如下:</div>
<div>/* net/ipv4/xfrm4_policy.c */<br>static struct dst_ops xfrm4_dst_ops = {<br>&nbsp;.family =&nbsp;&nbsp;AF_INET,<br>&nbsp;.protocol =&nbsp;&nbsp;__constant_htons(ETH_P_IP),<br>&nbsp;.gc =&nbsp;&nbsp;&nbsp;xfrm4_garbage_collect,<br>&nbsp;.update_pmtu =&nbsp;&nbsp;xfrm4_update_pmtu,<br>&nbsp;.destroy =&nbsp;&nbsp;xfrm4_dst_destroy,<br>&nbsp;.ifdown =&nbsp;&nbsp;xfrm4_dst_ifdown,<br>&nbsp;.gc_thresh =&nbsp;&nbsp;1024,<br>&nbsp;.entry_size =&nbsp;&nbsp;sizeof(struct xfrm_dst),<br>};</div>
<div><br>在xfrm_policy_register_afinfo()函数中, 还定义了安全路由操作结构的其他几个成员函数,因为这几个函数是和协议无关的, 所以在登记函数中定义了:</div>
<div>&nbsp;dst_ops-&gt;kmem_cachep = xfrm_dst_cache;<br>&nbsp;dst_ops-&gt;check = xfrm_dst_check;<br>&nbsp;dst_ops-&gt;negative_advice = xfrm_negative_advice;<br>&nbsp;dst_ops-&gt;link_failure = xfrm_link_failure;</div>
<div>&nbsp;</div>
<div>// 安全路由垃圾搜集, 就是调用安全策略信息结构的垃圾搜集函数<br>static inline int xfrm4_garbage_collect(void)<br>{<br>&nbsp;xfrm4_policy_afinfo.garbage_collect();<br>&nbsp;return (atomic_read(&amp;xfrm4_dst_ops.entries) &gt; xfrm4_dst_ops.gc_thresh*2);<br>}</div>
<div><br>// 更新路由的MTU<br>static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)<br>{<br>&nbsp;struct xfrm_dst *xdst = (struct xfrm_dst *)dst;<br>&nbsp;struct dst_entry *path = xdst-&gt;route;<br>// 调用的是安全路由的原始普通路由的MTU更新操作<br>&nbsp;path-&gt;ops-&gt;update_pmtu(path, mtu);<br>}</div>
<div><br>// 释放安全路由<br>static void xfrm4_dst_destroy(struct dst_entry *dst)<br>{<br>&nbsp;struct xfrm_dst *xdst = (struct xfrm_dst *)dst;<br>// 释放inet网卡引用<br>&nbsp;if (likely(xdst-&gt;u.rt.idev))<br>&nbsp;&nbsp;in_dev_put(xdst-&gt;u.rt.idev);<br>// 释放对方IP的引用<br>&nbsp;if (likely(xdst-&gt;u.rt.peer))<br>&nbsp;&nbsp;inet_putpeer(xdst-&gt;u.rt.peer);<br>// 释放安全路由<br>&nbsp;xfrm_dst_destroy(xdst);<br>}</div>
<div><br>&nbsp;static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)<br>&nbsp;{<br>&nbsp;// 释放和安全路由相关的普通路由<br>&nbsp;&nbsp;dst_release(xdst-&gt;route);<br>&nbsp;// 释放SA<br>&nbsp;&nbsp;if (likely(xdst-&gt;u.dst.xfrm))<br>&nbsp;&nbsp;&nbsp;xfrm_state_put(xdst-&gt;u.dst.xfrm);<br>&nbsp;}</div>
<div><br>// 网卡down时的回调操作<br>static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int unregister)<br>{<br>&nbsp;struct xfrm_dst *xdst;</div>
<div>&nbsp;if (!unregister)<br>&nbsp;&nbsp;return;</div>
<div>&nbsp;xdst = (struct xfrm_dst *)dst;<br>// 该安全路由对应的网卡是当前停掉的网卡<br>&nbsp;if (xdst-&gt;u.rt.idev-&gt;dev == dev) {<br>&nbsp;&nbsp;struct in_device *loopback_idev = in_dev_get(&amp;loopback_dev);<br>&nbsp;&nbsp;BUG_ON(!loopback_idev);</div>
<div>&nbsp;&nbsp;do {<br>// 释放安全路由网卡<br>&nbsp;&nbsp;&nbsp;in_dev_put(xdst-&gt;u.rt.idev);<br>// 安全路由网卡采用自身的回环网卡<br>&nbsp;&nbsp;&nbsp;xdst-&gt;u.rt.idev = loopback_idev;<br>&nbsp;&nbsp;&nbsp;in_dev_hold(loopback_idev);<br>// 子路由<br>&nbsp;&nbsp;&nbsp;xdst = (struct xfrm_dst *)xdst-&gt;u.dst.child;<br>&nbsp;&nbsp;} while (xdst-&gt;u.dst.xfrm);</div>
<div>&nbsp;&nbsp;__in_dev_put(loopback_idev);<br>&nbsp;}</div>
<div>&nbsp;xfrm_dst_ifdown(dst, dev);<br>}</div>
<div>&nbsp;</div>
<div>7.3 IPV4下的xfrm状态</div>
<div><br>IPV4下的xfrm状态在net/ipv4/xfrm4_state.c文件中定义, 主要是定义IPV4的状态信息结构:</div>
<div>static struct xfrm_state_afinfo xfrm4_state_afinfo = {<br>&nbsp;.family&nbsp;&nbsp;&nbsp;= AF_INET,<br>&nbsp;.init_flags&nbsp;&nbsp;= xfrm4_init_flags,<br>&nbsp;.init_tempsel&nbsp;&nbsp;= __xfrm4_init_tempsel,<br>};</div>
<div><br>该结构中在IPV4下只定义了两个处理函数:</div>
<div><br>// 初始化状态标志<br>static int xfrm4_init_flags(struct xfrm_state *x)<br>{<br>&nbsp;if (ipv4_config.no_pmtu_disc)<br>&nbsp;&nbsp;x-&gt;props.flags |= XFRM_STATE_NOPMTUDISC;<br>&nbsp;return 0;<br>}</div>
<div><br>// 初始化模板选择子<br>static void<br>__xfrm4_init_tempsel(struct xfrm_state *x, struct flowi *fl,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct xfrm_tmpl *tmpl,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xfrm_address_t *daddr, xfrm_address_t *saddr)<br>{<br>// 填写选择子信息<br>// 源地址<br>&nbsp;x-&gt;sel.daddr.a4 = fl-&gt;fl4_dst;<br>// 目的地址<br>&nbsp;x-&gt;sel.saddr.a4 = fl-&gt;fl4_src;<br>// 目的端口, 掩码<br>&nbsp;x-&gt;sel.dport = xfrm_flowi_dport(fl);<br>&nbsp;x-&gt;sel.dport_mask = htons(0xffff);<br>// 源端口掩码<br>&nbsp;x-&gt;sel.sport = xfrm_flowi_sport(fl);<br>&nbsp;x-&gt;sel.sport_mask = htons(0xffff);<br>// 源目的地址长度<br>&nbsp;x-&gt;sel.prefixlen_d = 32;<br>&nbsp;x-&gt;sel.prefixlen_s = 32;<br>// 协议<br>&nbsp;x-&gt;sel.proto = fl-&gt;proto;<br>// 网卡位置<br>&nbsp;x-&gt;sel.ifindex = fl-&gt;oif;<br>// 状态ID值<br>&nbsp;x-&gt;id = tmpl-&gt;id;<br>&nbsp;if (x-&gt;id.daddr.a4 == 0)<br>&nbsp;&nbsp;x-&gt;id.daddr.a4 = daddr-&gt;a4;<br>// 支持结构中的参数<br>// 源地址<br>&nbsp;x-&gt;props.saddr = tmpl-&gt;saddr;<br>&nbsp;if (x-&gt;props.saddr.a4 == 0)<br>&nbsp;&nbsp;x-&gt;props.saddr.a4 = saddr-&gt;a4;<br>// 模式<br>&nbsp;x-&gt;props.mode = tmpl-&gt;mode;<br>// 请求ID<br>&nbsp;x-&gt;props.reqid = tmpl-&gt;reqid;<br>// 协议族<br>&nbsp;x-&gt;props.family = AF_INET;<br>}</div>
<div><br>7.3小结</div>
<div><br>IPV4的状态信息结构中的相关成员函数的被调用关系可如下简单表示:</div>
<div>xfrm_init_state()<br>&nbsp; -&gt; afinfo-&gt;init_flags() == xfrm4_init_flags</div>
<div><br>xfrm_state_find()<br>&nbsp; -&gt; xfrm_init_tempsel()<br>&nbsp;&nbsp;&nbsp; -&gt; afinfo-&gt;init_tempsel() == __xfrm4_init_tempsel</div>
<div><br>7.4 模式</div>
<div><br>xfrm4支持3种模式: 通道, 传输和BEET模式, 分别在xfrm4_mode_tunnel.c, xfrm4_mode_transport.c和xfrm4_mode_beet.c中定义.</div>
<div>每个模式都通过结构struct xfrm_mode定义:<br>struct xfrm_mode {<br>&nbsp;int (*input)(struct xfrm_state *x, struct sk_buff *skb);<br>&nbsp;int (*output)(struct xfrm_state *x,struct sk_buff *skb);</div>
<div>&nbsp;struct module *owner;<br>&nbsp;unsigned int encap;<br>};</div>
<div>其中input函数在数据接收时调用, output函数数据发出时调用, encap参数表示是否封装.</div>
<div><br>7.4.1 通道</div>
<div>通道模式通过以下结构定义:</div>
<div>/* net/ipv4/xfrm4_mode_transport.c */</div>
<div>static struct xfrm_mode xfrm4_tunnel_mode = {<br>&nbsp;.input = xfrm4_tunnel_input,<br>&nbsp;.output = xfrm4_tunnel_output,<br>&nbsp;.owner = THIS_MODULE,<br>&nbsp;.encap = XFRM_MODE_TUNNEL,<br>};</div>
<div><br>// 通道模式下的接收函数, 解封装<br>static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)<br>{<br>&nbsp;struct iphdr *iph = skb-&gt;nh.iph;<br>&nbsp;int err = -EINVAL;</div>
<div>// IP协议为IPPROTO_IPIP(4)<br>&nbsp;if (iph-&gt;protocol != IPPROTO_IPIP)<br>&nbsp;&nbsp;goto out;<br>// 需要在skb头留出IP头的长度(20字节)<br>&nbsp;if (!pskb_may_pull(skb, sizeof(struct iphdr)))<br>&nbsp;&nbsp;goto out;</div>
<div>// 如果是clone包,重新拷贝一个<br>&nbsp;if (skb_cloned(skb) &amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp; (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))<br>&nbsp;&nbsp;goto out;<br>// 复制dscp字段<br>&nbsp;if (x-&gt;props.flags &amp; XFRM_STATE_DECAP_DSCP)<br>&nbsp;&nbsp;ipv4_copy_dscp(iph, skb-&gt;h.ipiph);<br>// 非XFRM_STATE_NOECN时进行ECN解封装<br>&nbsp;if (!(x-&gt;props.flags &amp; XFRM_STATE_NOECN))<br>&nbsp;&nbsp;ipip_ecn_decapsulate(skb);<br>// 将硬件地址挪到数据包缓冲区前<br>&nbsp;skb-&gt;mac.raw = memmove(skb-&gt;data - skb-&gt;mac_len,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; skb-&gt;mac.raw, skb-&gt;mac_len);<br>// 网络部分数据头<br>&nbsp;skb-&gt;nh.raw = skb-&gt;data;<br>&nbsp;err = 0;</div>
<div>out:<br>&nbsp;return err;<br>}</div>
<div><br>// 通道模式下的数据发出函数, 进行封装<br>static int xfrm4_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)<br>{<br>&nbsp;struct dst_entry *dst = skb-&gt;dst;<br>&nbsp;struct iphdr *iph, *top_iph;<br>&nbsp;int flags;</div>
<div>&nbsp;iph = skb-&gt;nh.iph;<br>&nbsp;skb-&gt;h.ipiph = iph;<br>// 数据头部增加外部IP头的长度<br>&nbsp;skb-&gt;nh.raw = skb_push(skb, x-&gt;props.header_len);<br>&nbsp;top_iph = skb-&gt;nh.iph;<br>// 填写外部IP头参数<br>&nbsp;top_iph-&gt;ihl = 5;<br>&nbsp;top_iph-&gt;version = 4;</div>
<div>&nbsp;/* DS disclosed */<br>// 重新计算TOS<br>&nbsp;top_iph-&gt;tos = INET_ECN_encapsulate(iph-&gt;tos, iph-&gt;tos);</div>
<div>&nbsp;flags = x-&gt;props.flags;<br>&nbsp;if (flags &amp; XFRM_STATE_NOECN)<br>&nbsp;&nbsp;IP_ECN_clear(top_iph);<br>// 处理分片包情况<br>&nbsp;top_iph-&gt;frag_off = (flags &amp; XFRM_STATE_NOPMTUDISC) ?<br>&nbsp;&nbsp;0 : (iph-&gt;frag_off &amp; htons(IP_DF));<br>&nbsp;if (!top_iph-&gt;frag_off)<br>&nbsp;&nbsp;__ip_select_ident(top_iph, dst-&gt;child, 0);<br>// TTL<br>&nbsp;top_iph-&gt;ttl = dst_metric(dst-&gt;child, RTAX_HOPLIMIT);<br>// 外部源地址用proposal中的源地址<br>&nbsp;top_iph-&gt;saddr = x-&gt;props.saddr.a4;<br>// 外部目的地址是SA中的目的地址<br>&nbsp;top_iph-&gt;daddr = x-&gt;id.daddr.a4;<br>// 外部IP头内的协议号为IPIP(4)<br>&nbsp;top_iph-&gt;protocol = IPPROTO_IPIP;<br>// IP选项部分设置为0<br>&nbsp;memset(&amp;(IPCB(skb)-&gt;opt), 0, sizeof(struct ip_options));<br>&nbsp;return 0;<br>}</div>
<div><br>7.4.2 传输</div>
<div><br>传输模式下不添加新的IP头, 其实几乎什么都不用做, 老点的2.6内核中就没有专门为传输模式定义.<br>传输模式结构定义为:</div>
<div>/* net/ipv4/xfrm4_mode_transport.c */</div>
<div>static struct xfrm_mode xfrm4_transport_mode = {<br>&nbsp;.input = xfrm4_transport_input,<br>&nbsp;.output = xfrm4_transport_output,<br>&nbsp;.owner = THIS_MODULE,<br>&nbsp;.encap = XFRM_MODE_TRANSPORT,<br>};</div>
<div><br>/* Remove encapsulation header.<br>&nbsp;*<br>&nbsp;* The IP header will be moved over the top of the encapsulation header.<br>&nbsp;*<br>&nbsp;* On entry, skb-&gt;h shall point to where the IP header should be and skb-&gt;nh<br>&nbsp;* shall be set to where the IP header currently is.&nbsp; skb-&gt;data shall point<br>&nbsp;* to the start of the payload.<br>&nbsp;*/<br>// 传输模式下的数据输入函数<br>static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)<br>{<br>// data指向负载头, h指向IP头, 但很多情况下两者相同<br>&nbsp;int ihl = skb-&gt;data - skb-&gt;h.raw;</div>
<div>// 如果h和nh不同, 将nh所指向IP头部分移动到h处<br>&nbsp;if (skb-&gt;h.raw != skb-&gt;nh.raw)<br>&nbsp;&nbsp;skb-&gt;nh.raw = memmove(skb-&gt;h.raw, skb-&gt;nh.raw, ihl);<br>// 增加数据包长度, 重新对数据包长度赋值<br>&nbsp;skb-&gt;nh.iph-&gt;tot_len = htons(skb-&gt;len + ihl);<br>&nbsp;skb-&gt;h.raw = skb-&gt;data;<br>&nbsp;return 0;<br>}</div>
<div>&nbsp;</div>
<div>/* Add encapsulation header.<br>&nbsp;*<br>&nbsp;* The IP header will be moved forward to make space for the encapsulation<br>&nbsp;* header.<br>&nbsp;*<br>&nbsp;* On exit, skb-&gt;h will be set to the start of the payload to be processed<br>&nbsp;* by x-&gt;type-&gt;output and skb-&gt;nh will be set to the top IP header.<br>&nbsp;*/<br>// 传输模式下的数据发出函数<br>static int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb)<br>{<br>&nbsp;struct iphdr *iph;<br>&nbsp;int ihl;<br>// nh和赋值给h<br>&nbsp;iph = skb-&gt;nh.iph;<br>&nbsp;skb-&gt;h.ipiph = iph;<br>// ip头长度<br>&nbsp;ihl = iph-&gt;ihl * 4;<br>// 重新计算h位置<br>&nbsp;skb-&gt;h.raw += ihl;<br>// 重新计算新的nh位置,增加proposal中的头长度, 拷贝原来的IP头数据<br>&nbsp;skb-&gt;nh.raw = memmove(skb_push(skb, x-&gt;props.header_len), iph, ihl);<br>&nbsp;return 0;<br>}</div>
<div><br>7.4.3 BEET</div>
<div><br>封装成BEETPH(94)包, 非标准IPSEC, 略.</div>
<div><br>7.4.4 小结</div>
<div>和xfrm_mode相关的xfrm函数有:<br>登记: int xfrm_register_mode(struct xfrm_mode *mode, int family);<br>撤销: int xfrm_unregister_mode(struct xfrm_mode *mode, int family)<br>获取: struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)<br>释放: void xfrm_put_mode(struct xfrm_mode *mode)</div>
<div><br>xfrm_mode的输入输出函数调用:</div>
<div>xfrm4_rcv_encap()<br>&nbsp; -&gt; x-&gt;mode-&gt;input</div>
<div>xfrm4_output_one()<br>&nbsp; -&gt; x-&gt;mode-&gt;output</div>
<div>&nbsp;</div>
<div>7.5 数据接收</div>
<div><br>IPV4的IPSEC数据接收处理在net/ipv4/xfrm4_input.c中定义, 作为AH和ESP协议数据接收处理函数.</div>
<div><br>/* net/ipv4/xfrm4_input.c */</div>
<div>int xfrm4_rcv(struct sk_buff *skb)<br>{<br>&nbsp;return xfrm4_rcv_encap(skb, 0);<br>}</div>
<div><br>实际就是xfrm4_rcv_encap,封装类型参数设置为0,在NAT-T时IPSEC数据被封装在UDP包中时, 该参数才非0.</div>
<div>int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)<br>{<br>&nbsp;int err;<br>&nbsp;__be32 spi, seq;<br>&nbsp;struct xfrm_state *xfrm_vec;<br>&nbsp;struct xfrm_state *x;<br>&nbsp;int xfrm_nr = 0;<br>&nbsp;int decaps = 0;</div>
<div>// 获取skb中的spi和序列号信息<br>&nbsp;if ((err = xfrm4_parse_spi(skb, skb-&gt;nh.iph-&gt;protocol, &amp;spi, &amp;seq)) != 0)<br>&nbsp;&nbsp;goto drop;</div>
<div>// 进入循环进行解包操作<br>&nbsp;do {<br>&nbsp;&nbsp;struct iphdr *iph = skb-&gt;nh.iph;</div>
<div>// 循环解包次数太深的话放弃<br>&nbsp;&nbsp;if (xfrm_nr == XFRM_MAX_DEPTH)<br>&nbsp;&nbsp;&nbsp;goto drop;<br>// 根据地址, SPI和协议查找SA<br>&nbsp;&nbsp;x = xfrm_state_lookup((xfrm_address_t *)&amp;iph-&gt;daddr, spi, iph-&gt;protocol, AF_INET);<br>&nbsp;&nbsp;if (x == NULL)<br>&nbsp;&nbsp;&nbsp;goto drop;</div>
<div>// 以下根据SA定义的操作对数据解码<br>&nbsp;&nbsp;spin_lock(&amp;x-&gt;lock);<br>&nbsp;&nbsp;if (unlikely(x-&gt;km.state != XFRM_STATE_VALID))<br>&nbsp;&nbsp;&nbsp;goto drop_unlock;</div>
<div>// 检查由SA指定的封装类型是否和函数指定的封装类型相同<br>&nbsp;&nbsp;if ((x-&gt;encap ? x-&gt;encap-&gt;encap_type : 0) != encap_type)<br>&nbsp;&nbsp;&nbsp;goto drop_unlock;</div>
<div>// SA重放窗口检查<br>&nbsp;&nbsp;if (x-&gt;props.replay_window &amp;&amp; xfrm_replay_check(x, seq))<br>&nbsp;&nbsp;&nbsp;goto drop_unlock;</div>
<div>// SA生存期检查<br>&nbsp;&nbsp;if (xfrm_state_check_expire(x))<br>&nbsp;&nbsp;&nbsp;goto drop_unlock;<br>// type可为esp,ah,ipcomp, ipip等, 对输入数据解密<br>&nbsp;&nbsp;if (x-&gt;type-&gt;input(x, skb))<br>&nbsp;&nbsp;&nbsp;goto drop_unlock;</div>
<div>&nbsp;&nbsp;/* only the first xfrm gets the encap type */<br>&nbsp;&nbsp;encap_type = 0;<br>// 更新重放窗口<br>&nbsp;&nbsp;if (x-&gt;props.replay_window)<br>&nbsp;&nbsp;&nbsp;xfrm_replay_advance(x, seq);<br>// 包数,字节数统计<br>&nbsp;&nbsp;x-&gt;curlft.bytes += skb-&gt;len;<br>&nbsp;&nbsp;x-&gt;curlft.packets++;</div>
<div>&nbsp;&nbsp;spin_unlock(&amp;x-&gt;lock);</div>
<div>// 保存数据解封用的SA, 增加SA数量计数<br>&nbsp;&nbsp;xfrm_vec = x;<br>// mode可为通道,传输等模式, 对输入数据解封装<br>&nbsp;&nbsp;if (x-&gt;mode-&gt;input(x, skb))<br>&nbsp;&nbsp;&nbsp;goto drop;</div>
<div>// 如果是IPSEC通道模式,将decaps参数置1,否则表示是传输模式<br>&nbsp;&nbsp;if (x-&gt;props.mode == XFRM_MODE_TUNNEL) {<br>&nbsp;&nbsp;&nbsp;decaps = 1;<br>&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;}<br>// 看内层协议是否还要继续解包, 不需要解时返回1, 需要解时返回0, 错误返回负数<br>// 协议类型可以多层封装的,比如用AH封装ESP, 就得先解完AH再解ESP<br>&nbsp;&nbsp;if ((err = xfrm_parse_spi(skb, skb-&gt;nh.iph-&gt;protocol, &amp;spi, &amp;seq)) &lt; 0)<br>&nbsp;&nbsp;&nbsp;goto drop;<br>&nbsp;} while (!err);</div>
<div>&nbsp;/* Allocate new secpath or COW existing one. */<br>// 为skb包建立新的安全路径(struct sec_path)<br>&nbsp;if (!skb-&gt;sp || atomic_read(&amp;skb-&gt;sp-&gt;refcnt) != 1) {<br>&nbsp;&nbsp;struct sec_path *sp;<br>&nbsp;&nbsp;sp = secpath_dup(skb-&gt;sp);<br>&nbsp;&nbsp;if (!sp)<br>&nbsp;&nbsp;&nbsp;goto drop;<br>&nbsp;&nbsp;if (skb-&gt;sp)<br>&nbsp;&nbsp;&nbsp;secpath_put(skb-&gt;sp);<br>&nbsp;&nbsp;skb-&gt;sp = sp;<br>&nbsp;}<br>&nbsp;if (xfrm_nr + skb-&gt;sp-&gt;len &gt; XFRM_MAX_DEPTH)<br>&nbsp;&nbsp;goto drop;<br>// 将刚才循环解包用到的SA拷贝到安全路径<br>// 因此检查一个数据包是否是普通明文包还是解密后的明文包就看skb-&gt;sp参数是否为空<br>&nbsp;memcpy(skb-&gt;sp-&gt;xvec + skb-&gt;sp-&gt;len, xfrm_vec,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xfrm_nr * sizeof(xfrm_vec));<br>&nbsp;skb-&gt;sp-&gt;len += xfrm_nr;</div>
<div>&nbsp;nf_reset(skb);</div>
<div>&nbsp;if (decaps) {<br>// 通道模式<br>&nbsp;&nbsp;if (!(skb-&gt;dev-&gt;flags&amp;IFF_LOOPBACK)) {<br>&nbsp;&nbsp;&nbsp;dst_release(skb-&gt;dst);<br>&nbsp;&nbsp;&nbsp;skb-&gt;dst = NULL;<br>&nbsp;&nbsp;}<br>// 重新进入网卡接收函数<br>&nbsp;&nbsp;netif_rx(skb);<br>&nbsp;&nbsp;return 0;<br>&nbsp;} else {<br>// 传输模式<br>#ifdef CONFIG_NETFILTER<br>// 如果定义NETFILTER, 进入PRE_ROUTING链处理,然后进入路由选择处理<br>// 其实现在已经处于INPUT点, 但解码后需要将该包作为一个新包看待<br>// 可能需要进行目的NAT操作, 这时候可能目的地址就会改变不是到自身<br>// 的了, 因此需要将其相当于是放回PRE_PROUTING点去操作, 重新找路由<br>// 这也说明可以制定针对解码后明文包的NAT规则,在还是加密包的时候不匹配<br>// 但解码后能匹配上<br>&nbsp;&nbsp;__skb_push(skb, skb-&gt;data - skb-&gt;nh.raw);<br>&nbsp;&nbsp;skb-&gt;nh.iph-&gt;tot_len = htons(skb-&gt;len);<br>&nbsp;&nbsp;ip_send_check(skb-&gt;nh.iph);<br>&nbsp;&nbsp;NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb-&gt;dev, NULL,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xfrm4_rcv_encap_finish);<br>&nbsp;&nbsp;return 0;<br>#else<br>// 内核不支持NETFILTER, 该包肯定就是到自身的了<br>// 返回IP协议的负值, 表示重新进行IP层协议的处理<br>// 用解码后的内层协议来处理数据<br>&nbsp;&nbsp;return -skb-&gt;nh.iph-&gt;protocol;<br>#endif<br>&nbsp;}</div>
<div>drop_unlock:<br>&nbsp;spin_unlock(&amp;x-&gt;lock);<br>&nbsp;xfrm_state_put(x);<br>drop:<br>&nbsp;while (--xfrm_nr &gt;= 0)<br>&nbsp;&nbsp;xfrm_state_put(xfrm_vec);</div>
<div>&nbsp;kfree_skb(skb);<br>&nbsp;return 0;<br>}</div>
<div>// 解析AH,ESP数据包中的SPI和序号<br>static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)<br>{<br>&nbsp;switch (nexthdr) {<br>// 如果只是普通的IPIP包, SPI为源地址, 序号位0<br>&nbsp;case IPPROTO_IPIP:<br>&nbsp;&nbsp;*spi = skb-&gt;nh.iph-&gt;saddr;<br>&nbsp;&nbsp;*seq = 0;<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>// 否则解析AH/ESP/COMP协议头中的SPI和序号<br>&nbsp;return xfrm_parse_spi(skb, nexthdr, spi, seq);<br>}</div>
<div>// 接收封装完成处理函数<br>static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb)<br>{<br>&nbsp;struct iphdr *iph = skb-&gt;nh.iph;</div>
<div>// 如果没有路由, 重新查找路由<br>&nbsp;if (skb-&gt;dst == NULL) {<br>&nbsp;&nbsp;if (ip_route_input(skb, iph-&gt;daddr, iph-&gt;saddr, iph-&gt;tos,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; skb-&gt;dev))<br>&nbsp;&nbsp;&nbsp;goto drop;<br>&nbsp;}<br>// 调用相关的路由输入函数<br>&nbsp;return dst_input(skb);<br>drop:<br>&nbsp;kfree_skb(skb);<br>&nbsp;return NET_RX_DROP;<br>}</div>
<div>&nbsp;</div>
<div>调用关系:</div>
<div>ip_rcv<br>&nbsp; -&gt; (AH/ESP) net_protocol-&gt;handler == xfrm4_rcv<br>&nbsp;&nbsp;&nbsp; -&gt; xfrm4_rcv_encap<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm4_parse_spi<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm_parse_spi<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm4_rcv_encap_finish</div>
<div>&nbsp;</div>
<div>7.6 数据发送</div>
<div><br>IPV4的IPSEC数据发送处理在net/ipv4/xfrm4_output.c中定义,作为安全路由的输出函数:</div>
<div><br>int xfrm4_output(struct sk_buff *skb)<br>{<br>// 就是一个条件HOOK, 当skb包不带IPSKB_REROUTED标志时进入POSTROUTING点的NAT操作<br>// 这是数据在xfrm策略中多个bundle时会多次调用, 也就是数据在封装完成前可以进行<br>// 源NAT操作<br>// HOOK出口函数为xfrm4_output_finish<br>&nbsp;return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb-&gt;dst-&gt;dev,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xfrm4_output_finish,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; !(IPCB(skb)-&gt;flags &amp; IPSKB_REROUTED));<br>}</div>
<div><br>// 发送结束处理<br>static int xfrm4_output_finish(struct sk_buff *skb)<br>{<br>&nbsp;struct sk_buff *segs;</div>
<div>#ifdef CONFIG_NETFILTER<br>// 如果内核定义了NETFILTER, 当到达最后一个路由(普通路由)时, 设置IPSKB_REROUTED<br>// 标志, 进行普通路由发出函数(ip_output), 设置该标志后不进行源NAT操作<br>&nbsp;if (!skb-&gt;dst-&gt;xfrm) {<br>&nbsp;&nbsp;IPCB(skb)-&gt;flags |= IPSKB_REROUTED;<br>&nbsp;&nbsp;return dst_output(skb);<br>&nbsp;}<br>#endif<br>// 如果skb包不是是gso, 转xfrm4_output_finish2<br>// gso是什么意思现在还不知道, 以后再仔细分析<br>&nbsp;if (!skb_is_gso(skb))<br>&nbsp;&nbsp;return xfrm4_output_finish2(skb);</div>
<div>// 处理gso数据包, 最终也是使用xfrm4_output_finish2处理数据包<br>&nbsp;skb-&gt;protocol = htons(ETH_P_IP);<br>&nbsp;segs = skb_gso_segment(skb, 0);<br>&nbsp;kfree_skb(skb);<br>&nbsp;if (unlikely(IS_ERR(segs)))<br>&nbsp;&nbsp;return PTR_ERR(segs);</div>
<div>&nbsp;do {<br>&nbsp;&nbsp;struct sk_buff *nskb = segs-&gt;next;<br>&nbsp;&nbsp;int err;</div>
<div>&nbsp;&nbsp;segs-&gt;next = NULL;<br>&nbsp;&nbsp;err = xfrm4_output_finish2(segs);</div>
<div>&nbsp;&nbsp;if (unlikely(err)) {<br>&nbsp;&nbsp;&nbsp;while ((segs = nskb)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;nskb = segs-&gt;next;<br>&nbsp;&nbsp;&nbsp;&nbsp;segs-&gt;next = NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;kfree_skb(segs);<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;return err;<br>&nbsp;&nbsp;}</div>
<div>&nbsp;&nbsp;segs = nskb;<br>&nbsp;} while (segs);</div>
<div>&nbsp;return 0;<br>}</div>
<div>&nbsp;</div>
<div>// 第2级发送结束处理<br>static int xfrm4_output_finish2(struct sk_buff *skb)<br>{<br>&nbsp;int err;<br>// 根据安全路由包装要发送数据<br>&nbsp;while (likely((err = xfrm4_output_one(skb)) == 0)) {<br>// 处理成功<br>// 释放skb中的netfilter信息<br>&nbsp;&nbsp;nf_reset(skb);<br>// 重新将该包作为初始发送包, 进入OUTPUT点处理, 注意这是个函数而不是宏<br>// 如果内核没定义NETFILTER, 该函数只是个空函数<br>// 返回1表示NF_ACCEPT<br>&nbsp;&nbsp;err = nf_hook(PF_INET, NF_IP_LOCAL_OUT, &amp;skb, NULL,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; skb-&gt;dst-&gt;dev, dst_output);<br>&nbsp;&nbsp;if (unlikely(err != 1))<br>&nbsp;&nbsp;&nbsp;break;<br>// 如果已经没有SA, 就只是个普通包了, 路由发送(ip_output)返回, 退出循环<br>&nbsp;&nbsp;if (!skb-&gt;dst-&gt;xfrm)<br>&nbsp;&nbsp;&nbsp;return dst_output(skb);</div>
<div>// 如果还有SA, 目前还只是中间状态, 还可以进行SNAT操作, 进入POSTROUTING点处理<br>&nbsp;&nbsp;err = nf_hook(PF_INET, NF_IP_POST_ROUTING, &amp;skb, NULL,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; skb-&gt;dst-&gt;dev, xfrm4_output_finish2);<br>&nbsp;&nbsp;if (unlikely(err != 1))<br>&nbsp;&nbsp;&nbsp;break;<br>&nbsp;}</div>
<div>&nbsp;return err;<br>}</div>
<div><br>// 按安全路由链表的安全路由处理数据, 该链表反映了多个SA对数据包进行处理<br>// 链表是在__xfrm4_bundle_create函数中建立的<br>static int xfrm4_output_one(struct sk_buff *skb)<br>{<br>// 安全路由<br>&nbsp;struct dst_entry *dst = skb-&gt;dst;<br>// 相关SA<br>&nbsp;struct xfrm_state *x = dst-&gt;xfrm;<br>&nbsp;int err;<br>// skb包校验和&nbsp;检查<br>&nbsp;if (skb-&gt;ip_summed == CHECKSUM_PARTIAL) {<br>&nbsp;&nbsp;err = skb_checksum_help(skb);<br>&nbsp;&nbsp;if (err)<br>&nbsp;&nbsp;&nbsp;goto error_nolock;<br>&nbsp;}</div>
<div>// 如果是通道模式, 检查skb数据长度, 并进行相关处理, 通道模式下封装后的数据包长度可能<br>// 会超过1500字节的<br>&nbsp;if (x-&gt;props.mode == XFRM_MODE_TUNNEL) {<br>&nbsp;&nbsp;err = xfrm4_tunnel_check_size(skb);<br>&nbsp;&nbsp;if (err)<br>&nbsp;&nbsp;&nbsp;goto error_nolock;<br>&nbsp;}</div>
<div>&nbsp;do {<br>&nbsp;&nbsp;spin_lock_bh(&amp;x-&gt;lock);<br>// SA合法性检查<br>&nbsp;&nbsp;err = xfrm_state_check(x, skb);<br>&nbsp;&nbsp;if (err)<br>&nbsp;&nbsp;&nbsp;goto error;<br>// 调用模式输出函数, 如通道封装, 此时外部IP头协议为IPIP<br>&nbsp;&nbsp;err = x-&gt;mode-&gt;output(x, skb);<br>&nbsp;&nbsp;if (err)<br>&nbsp;&nbsp;&nbsp;goto error;</div>
<div>// 调用协议输出, 如对应ESP协议来说是esp4_output, 此时外部IP头协议会改为ESP<br>&nbsp;&nbsp;err = x-&gt;type-&gt;output(x, skb);<br>&nbsp;&nbsp;if (err)<br>&nbsp;&nbsp;&nbsp;goto error;<br>// 更新SA中的当前生命期结构中的包和字节计数<br>&nbsp;&nbsp;x-&gt;curlft.bytes += skb-&gt;len;<br>&nbsp;&nbsp;x-&gt;curlft.packets++;</div>
<div>&nbsp;&nbsp;spin_unlock_bh(&amp;x-&gt;lock);<br>// 转移到下一个子路由&nbsp;<br>&nbsp;&nbsp;if (!(skb-&gt;dst = dst_pop(dst))) {<br>&nbsp;&nbsp;&nbsp;err = -EHOSTUNREACH;<br>&nbsp;&nbsp;&nbsp;goto error_nolock;<br>&nbsp;&nbsp;}<br>// dst和x参数更新为子路由中的安全路由和SA<br>&nbsp;&nbsp;dst = skb-&gt;dst;<br>&nbsp;&nbsp;x = dst-&gt;xfrm;<br>// 循环条件是SA非空, 而且SA提议模式不是通道模式<br>&nbsp;} while (x &amp;&amp; (x-&gt;props.mode != XFRM_MODE_TUNNEL));<br>// skb中设置IPSKB_XFRM_TRANSFORMED标志<br>// 有该标志的数据包将NAT操作后将不进行一些特殊检查<br>&nbsp;IPCB(skb)-&gt;flags |= IPSKB_XFRM_TRANSFORMED;<br>&nbsp;err = 0;</div>
<div>out_exit:<br>&nbsp;return err;<br>error:<br>&nbsp;spin_unlock_bh(&amp;x-&gt;lock);<br>error_nolock:<br>&nbsp;kfree_skb(skb);<br>&nbsp;goto out_exit;<br>}</div>
<div><br>IPSEC输出函数调用关系:</div>
<div>dst_output<br>&nbsp; -&gt; xfrm_dst-&gt;output == xfrm4_output<br>&nbsp;&nbsp;&nbsp;&nbsp; -&gt; NF_HOOK(POSTROUTING)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm4_output_finish<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm4_output_finish2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm4_output_one</div>
<div><br>7.7 NAT-T支持</div>
<div><br>在支持NAT穿越的IPSEC处理中,是通过UDP数据包来封装IPSEC数据(ESP数据包),因此在对UDP处理时需要进行特殊处
理。由于IKE同样是用UDP处理的, 区分是IKE包还是封装的ESP包就看数据头部头4字节表示的SPI值, SPI为0表示是IKE包,
由IKE用户空间程序接收进行处理, SPI非0表示是UDP封装的ESP包, 需进行ESP解封。</div>
<div><br>7.7.1 接收数据</div>
<div><br>被UDP封装的IPSEC包在接收时会先按普通UDP包接收,在UDP处理中再解开该包后进行IPSEC处理</div>
<div>/* net/ipv4/udp.c */<br>// 正常接收的UDP包都将进入该函数<br>static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)<br>{<br>&nbsp;struct udp_sock *up = udp_sk(sk);<br>&nbsp;int rc;</div>
<div>&nbsp;/*<br>&nbsp; *&nbsp;Charge it to the socket, dropping if the queue is full.<br>&nbsp; */<br>// 检查针对该sock,skb包的输入方法上的是否有安全策略<br>&nbsp;if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) {<br>&nbsp;&nbsp;kfree_skb(skb);<br>&nbsp;&nbsp;return -1;<br>&nbsp;}<br>&nbsp;nf_reset(skb);</div>
<div>// 检查该SOCK是否是IPSEC封装的,该参数通过setsockopt系统调用的UDP_ENCAP选项设置<br>// 一般是IKE程序在打开UDP4500端口时设置的<br>&nbsp;if (up-&gt;encap_type) {<br>&nbsp;&nbsp;/*<br>&nbsp;&nbsp; * This is an encapsulation socket, so let's see if this is<br>&nbsp;&nbsp; * an encapsulated packet.<br>&nbsp;&nbsp; * If it's a keepalive packet, then just eat it.<br>&nbsp;&nbsp; * If it's an encapsulateed packet, then pass it to the<br>&nbsp;&nbsp; * IPsec xfrm input and return the response<br>&nbsp;&nbsp; * appropriately.&nbsp; Otherwise, just fall through and<br>&nbsp;&nbsp; * pass this up the UDP socket.<br>&nbsp;&nbsp; */<br>&nbsp;&nbsp;int ret;<br>// 进入UDP封装接收, 判断是否是ESP包<br>// 返回值小于0表示是IPSEC包, 大于0表示是普通UDP包, 等于0表示是错误包<br>&nbsp;&nbsp;ret = udp_encap_rcv(sk, skb);<br>&nbsp;&nbsp;if (ret == 0) {<br>&nbsp;&nbsp;&nbsp;/* Eat the packet .. */<br>&nbsp;&nbsp;&nbsp;kfree_skb(skb);<br>&nbsp;&nbsp;&nbsp;return 0;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;if (ret &lt; 0) {<br>// 进行IPSEC接收处理<br>&nbsp;&nbsp;&nbsp;/* process the ESP packet */<br>&nbsp;&nbsp;&nbsp;ret = xfrm4_rcv_encap(skb, up-&gt;encap_type);<br>&nbsp;&nbsp;&nbsp;UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS);<br>&nbsp;&nbsp;&nbsp;return -ret;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;/* FALLTHROUGH -- it's a UDP Packet */<br>&nbsp;}<br>// 以下按普通UDP包接收处理, 分析略<br>&nbsp;if (sk-&gt;sk_filter &amp;&amp; skb-&gt;ip_summed != CHECKSUM_UNNECESSARY) {<br>&nbsp;&nbsp;if (__udp_checksum_complete(skb)) {<br>&nbsp;&nbsp;&nbsp;UDP_INC_STATS_BH(UDP_MIB_INERRORS);<br>&nbsp;&nbsp;&nbsp;kfree_skb(skb);<br>&nbsp;&nbsp;&nbsp;return -1;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;skb-&gt;ip_summed = CHECKSUM_UNNECESSARY;<br>&nbsp;}</div>
<div>&nbsp;if ((rc = sock_queue_rcv_skb(sk,skb)) &lt; 0) {<br>&nbsp;&nbsp;/* Note that an ENOMEM error is charged twice */<br>&nbsp;&nbsp;if (rc == -ENOMEM)<br>&nbsp;&nbsp;&nbsp;UDP_INC_STATS_BH(UDP_MIB_RCVBUFERRORS);<br>&nbsp;&nbsp;UDP_INC_STATS_BH(UDP_MIB_INERRORS);<br>&nbsp;&nbsp;kfree_skb(skb);<br>&nbsp;&nbsp;return -1;<br>&nbsp;}<br>&nbsp;UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS);<br>&nbsp;return 0;<br>}</div>
<div><br>/* return:<br>&nbsp;* &nbsp;1&nbsp; if the the UDP system should process it<br>&nbsp;*&nbsp;0&nbsp; if we should drop this packet<br>&nbsp;* &nbsp;-1 if it should get processed by xfrm4_rcv_encap<br>&nbsp;*/<br>static int udp_encap_rcv(struct sock * sk, struct sk_buff *skb)<br>{<br>#ifndef CONFIG_XFRM<br>// 在内核不支持IPSEC情况下直接返回1<br>&nbsp;return 1; <br>#else<br>&nbsp;struct udp_sock *up = udp_sk(sk);<br>&nbsp; &nbsp;struct udphdr *uh;<br>&nbsp;struct iphdr *iph;<br>&nbsp;int iphlen, len;<br>&nbsp; <br>&nbsp;__u8 *udpdata;<br>&nbsp;__be32 *udpdata32;<br>// sock的封装标志值<br>&nbsp;__u16 encap_type = up-&gt;encap_type;</div>
<div>&nbsp;/* if we're overly short, let UDP handle it */<br>// UDP数据包中数据部分的长度<br>&nbsp;len = skb-&gt;len - sizeof(struct udphdr);<br>&nbsp;if (len &lt;= 0)<br>&nbsp;&nbsp;return 1;</div>
<div>&nbsp;/* if this is not encapsulated socket, then just return now */<br>// 没定义封装处理, 返回1, 普通处理<br>&nbsp;if (!encap_type)<br>&nbsp;&nbsp;return 1;</div>
<div>&nbsp;/* If this is a paged skb, make sure we pull up<br>&nbsp; * whatever data we need to look at. */<br>&nbsp;if (!pskb_may_pull(skb, sizeof(struct udphdr) + min(len, 8)))<br>&nbsp;&nbsp;return 1;</div>
<div>&nbsp;/* Now we can get the pointers */<br>&nbsp;uh = skb-&gt;h.uh;<br>&nbsp;udpdata = (__u8 *)uh + sizeof(struct udphdr);<br>&nbsp;udpdata32 = (__be32 *)udpdata;</div>
<div>&nbsp;switch (encap_type) {<br>&nbsp;default:<br>// 在UDP中封装ESP<br>&nbsp;case UDP_ENCAP_ESPINUDP:<br>&nbsp;&nbsp;/* Check if this is a keepalive packet.&nbsp; If so, eat it. */<br>&nbsp;&nbsp;if (len == 1 &amp;&amp; udpdata == 0xff) {<br>// 只是普通UDP的IPSEC通道保活包, 直接丢弃<br>&nbsp;&nbsp;&nbsp;return 0;<br>&nbsp;&nbsp;} else if (len &gt; sizeof(struct ip_esp_hdr) &amp;&amp; udpdata32 != 0 ) {<br>// 头4字节非零, ESP包,需要下一步解析<br>&nbsp;&nbsp;&nbsp;/* ESP Packet without Non-ESP header */<br>&nbsp;&nbsp;&nbsp;len = sizeof(struct udphdr);<br>&nbsp;&nbsp;} else<br>// 这是IKE包,按普通UDP接收处理<br>&nbsp;&nbsp;&nbsp;/* Must be an IKE packet.. pass it through */<br>&nbsp;&nbsp;&nbsp;return 1;<br>&nbsp;&nbsp;break;<br>&nbsp;case UDP_ENCAP_ESPINUDP_NON_IKE:<br>&nbsp;&nbsp;/* Check if this is a keepalive packet.&nbsp; If so, eat it. */<br>&nbsp;&nbsp;if (len == 1 &amp;&amp; udpdata == 0xff) {<br>// IPSEC通道保活包, 丢弃<br>&nbsp;&nbsp;&nbsp;return 0;<br>&nbsp;&nbsp;} else if (len &gt; 2 * sizeof(u32) + sizeof(struct ip_esp_hdr) &amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; udpdata32 == 0 &amp;&amp; udpdata32 == 0) {<br>// 头4字节非零, ESP包,需要下一步解析<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;/* ESP Packet with Non-IKE marker */<br>&nbsp;&nbsp;&nbsp;len = sizeof(struct udphdr) + 2 * sizeof(u32);<br>&nbsp;&nbsp;} else<br>// 这是IKE数据包,由<br>&nbsp;&nbsp;&nbsp;/* Must be an IKE packet.. pass it through */<br>&nbsp;&nbsp;&nbsp;return 1;<br>&nbsp;&nbsp;break;<br>&nbsp;}</div>
<div>&nbsp;/* At this point we are sure that this is an ESPinUDP packet,<br>&nbsp; * so we need to remove 'len' bytes from the packet (the UDP<br>&nbsp; * header and optional ESP marker bytes) and then modify the<br>&nbsp; * protocol to ESP, and then call into the transform receiver.<br>&nbsp; */<br>// 如果是clone包需要复制成独立包<br>&nbsp;if (skb_cloned(skb) &amp;&amp; pskb_expand_head(skb, 0, 0, GFP_ATOMIC))<br>&nbsp;&nbsp;return 0;</div>
<div>// 检查数据长度<br>&nbsp;/* Now we can update and verify the packet length... */<br>&nbsp;iph = skb-&gt;nh.iph;<br>&nbsp;iphlen = iph-&gt;ihl &lt;&lt; 2;<br>&nbsp;iph-&gt;tot_len = htons(ntohs(iph-&gt;tot_len) - len);<br>&nbsp;if (skb-&gt;len &lt; iphlen + len) {<br>&nbsp;&nbsp;/* packet is too small!?! */<br>&nbsp;&nbsp;return 0;<br>&nbsp;}</div>
<div>&nbsp;/* pull the data buffer up to the ESP header and set the<br>&nbsp; * transport header to point to ESP.&nbsp; Keep UDP on the stack<br>&nbsp; * for later.<br>&nbsp; */<br>// 修改IP上层头位置<br>&nbsp;skb-&gt;h.raw = skb_pull(skb, len);<br>// 更改IP头协议类型为ESP包, 返回-1<br>&nbsp;/* modify the protocol (it's ESP!) */<br>&nbsp;iph-&gt;protocol = IPPROTO_ESP;</div>
<div>&nbsp;/* and let the caller know to send this into the ESP processor... */<br>&nbsp;return -1;<br>#endif<br>}</div>
<div><br>函数调用关系:</div>
<div>udp_rcv<br>&nbsp; -&gt;udp_queue_rcv_skb<br>&nbsp;&nbsp;&nbsp; -&gt; udp_encap_rcv<br>&nbsp;&nbsp;&nbsp; -&gt; xfrm4_policy_check<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; xfrm_policy_check<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; __xfrm_policy_check<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </div>
<div>7.7.2 ESP包的UDP封装</div>
<div><br>对于ESP包的UDP封装处理, 在下一节ESP协议数据包的输出处理中介绍.</div>
<div><br>...... 待续 ......</div>
</div>
</td></tr>
</tbody></table>
<p style="line-height: 150%; margin: 5px;">


</p>
</td></tr>


<tr><td height="25" align="center">
<font color="#295200">发表于: 2007-06-17,修改于: 2007-06-17 16:02,已浏览3682次,有评论1条</font> <a href="http://blogold.chinaunix.net/u2/star.php?blogid=12313&amp;artid=323050" id="star" title="推荐这篇文章" target="_blank">推荐</a>
<a href="http://blogold.chinaunix.net/u2/complaint.php?blogid=12313&amp;artid=323050" id="complaint" title="投诉这篇文章" target="_blank">投诉</a>
</td></tr>

<tr><td height="9" align="center"><img src="http://blogold.chinaunix.net/templates/default/images/line1.gif" alt="" height="13" width="702" border="0"></td></tr>
<tr><td>
<table style="border-collapse: collapse; color: rgb(2, 54, 141);" width="95%" align="center" border="0" cellpadding="0" cellspacing="0">
<tbody><tr height="25">
<td width="25"><img src="http://blogold.chinaunix.net/templates/default/images/dot3.gif" alt="" height="16" width="16" border="0"></td>
<td width="220">网友:<b>
<a href="jump/byuser.php?username=jewsus" target="_blank" title="查看jewsus的BLOG" target="_blank">jewsus</a>
</b> </td><td style="color: rgb(170, 170, 170);" width="455" align="right">时间:2008-11-26 18:06:30    IP地址:122.9.55.★</td></tr>
<tr><td colspan="3" height="1" bgcolor="#91bbed"><br></td></tr>
<tr><td colspan="3" height="5"><br></td></tr>
<tr>
<td colspan="3">
<table style="border-collapse: collapse; word-wrap: break-word; color: rgb(2, 54, 141);" width="500" border="0" cellpadding="0" cellspacing="0">
<tbody><tr height="30"><td valign="top" width="20"><br></td><td valign="top" width="480">
Linux内核中ipsec的dst_output处理过程,当执行第一个dst_output时,这时会进入&nbsp;xfrm4_outpot,下面是这个函数的代码:<br>
<br>
int&nbsp;xfrm4_output(struct&nbsp;sk_buff&nbsp;*skb)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;NF_HOOK_COND(PF_INET,&nbsp;NF_IP_POST_ROUTING,&nbsp;skb,&nbsp;NULL,&nbsp;skb-&gt;dst-&gt;dev,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xfrm4_output_finish,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;!(IPCB(skb)-&gt;flags&nbsp;&amp;&nbsp;IPSKB_REROUTED));<br>
}<br>
<br>
<br>
static&nbsp;int&nbsp;xfrm4_output_finish(struct&nbsp;sk_buff&nbsp;*skb)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;err;<br>
<br>
#ifdef&nbsp;CONFIG_NETFILTER<br>
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!skb-&gt;dst-&gt;xfrm)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IPCB(skb)-&gt;flags&nbsp;|=&nbsp;IPSKB_REROUTED;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;dst_output(skb);<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
#endif<br>
&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(likely((err&nbsp;=&nbsp;xfrm4_output_one(skb))&nbsp;==&nbsp;0))&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nf_reset(skb);<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;nf_hook(PF_INET,&nbsp;NF_IP_LOCAL_OUT,&nbsp;&amp;skb,&nbsp;NULL,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;skb-&gt;dst-&gt;dev,&nbsp;dst_output);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(unlikely(err&nbsp;!=&nbsp;1))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!skb-&gt;dst-&gt;xfrm)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;dst_output(skb);<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;nf_hook(PF_INET,&nbsp;NF_IP_POST_ROUTING,&nbsp;&amp;skb,&nbsp;NULL,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;skb-&gt;dst-&gt;dev,&nbsp;xfrm4_output_finish);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(unlikely(err&nbsp;!=&nbsp;1))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;err;<br>
}<br>
<br>
static&nbsp;int&nbsp;xfrm4_output_one(struct&nbsp;sk_buff&nbsp;*skb)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;dst_entry&nbsp;*dst&nbsp;=&nbsp;skb-&gt;dst;<br>
&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;xfrm_state&nbsp;*x&nbsp;=&nbsp;dst-&gt;xfrm;<br>
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;err;<br>
&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(skb-&gt;ip_summed&nbsp;==&nbsp;CHECKSUM_HW)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;skb_checksum_help(skb,&nbsp;0);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(err)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;error_nolock;<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(x-&gt;props.mode)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;xfrm4_tunnel_check_size(skb);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(err)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;error_nolock;<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;do&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spin_lock_bh(&amp;x-&gt;lock);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;xfrm_state_check(x,&nbsp;skb);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(err)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;error;<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xfrm4_encap(skb);<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;x-&gt;type-&gt;output(x,&nbsp;skb);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(err)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;error;<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x-&gt;curlft.bytes&nbsp;+=&nbsp;skb-&gt;len;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x-&gt;curlft.packets++;<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spin_unlock_bh(&amp;x-&gt;lock);<br>
&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!(skb-&gt;dst&nbsp;=&nbsp;dst_pop(dst)))&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;-EHOSTUNREACH;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;error_nolock;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dst&nbsp;=&nbsp;skb-&gt;dst;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x&nbsp;=&nbsp;dst-&gt;xfrm;<br>
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;while&nbsp;(x&nbsp;&amp;&amp;&nbsp;!x-&gt;props.mode);<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;IPCB(skb)-&gt;flags&nbsp;|=&nbsp;IPSKB_XFRM_TRANSFORMED;<br>
&nbsp;&nbsp;&nbsp;&nbsp;err&nbsp;=&nbsp;0;<br>
<br>
out_exit:<br>
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;err;<br>
error:<br>
&nbsp;&nbsp;&nbsp;&nbsp;spin_unlock_bh(&amp;x-&gt;lock);<br>
error_nolock:<br>
&nbsp;&nbsp;&nbsp;&nbsp;kfree_skb(skb);<br>
&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;out_exit;<br>
}<br>
<br>
这段代码中嵌套调用了很多NF_HOOK_COND&nbsp;nf_hook函数,他最终是怎么推出循环调用到最后的ip_output的呢?在xfrm4_output_finish首先运行&nbsp;xfrm4_output_one执行了esp_output和ah_output进行了完整性认证,为什么后面还可以调用&nbsp;nf_hook(PF_INET,&nbsp;NF_IP_LOCAL_OUT,&nbsp;&amp;skb,&nbsp;NULL,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;skb-&gt;dst-&gt;dev,&nbsp;dst_output);和nf_hook(PF_INET,&nbsp;NF_IP_POST_ROUTING,&nbsp;&amp;skb,&nbsp;NULL,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;skb-&gt;dst-&gt;dev,&nbsp;xfrm4_output_finish);进行NAT操作?我觉得在这里没必要调用nf_hook函数去走重新遍历hook链表。</td></tr></tbody></table></td></tr></tbody></table></td></tr></tbody></table>
页: [1]
查看完整版本: Linux内核中的IPSEC实现(5)