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