日期:2014-05-16 浏览次数:20752 次
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 =