- 浏览: 316026 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
JQ_AK47:
...
Linux下直接发送以太包 -
winsen2009:
谢谢分享,如果能再来一个列子就更好了,刚接触看完还是不懂的用
UNPv1_r3读书笔记: SCTP编程
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
1. 前言 在Linux2.6中自带了ipsec的实现,可以不再使用freeswan及其变种了,freeswan通过建立ipsec*的 虚拟网卡来将发送和接收ipsec数据包,通过ipsec*网卡看到的数据是明文数据,而2.6中的ipsec实 现是不建立ipsec*虚拟网卡的,本文分析一下ESP包进入系统协议栈的处理流程。 以下Linux内核代码版本为2.6.19.2。 2. 流程分析 2.1 esp协议结构 esp协议结构定义,对于每个IPv4上层的协议,如TCP、UDP、ICMP、IGMP、ESP、AH等都需要定义这个 结构挂接到IPv4的协议链表中,当接收到IP数据包时,会根据包中定义的IP协议号找到该结构,然后 调用其成员handler函数进行处理。 /* net/ipv4/esp4.c */ static struct net_protocol esp4_protocol = { .handler = xfrm4_rcv, .err_handler = esp4_err, .no_policy = 1, }; esp协议的handler函数是xfrm4_rcv() 2.2 xfrm4_rcv /* net/ipv4/xfrm4_input.c */ int xfrm4_rcv(struct sk_buff *skb) { return xfrm4_rcv_encap(skb, 0); } 实际就是xfrm4_rcv_encap,封装类型参数设置为0,即没封装数据 2.3 xfrm4_rcv_encap /* net/ipv4/xfrm4_input.c */ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) { int err; __be32 spi, seq; struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; struct xfrm_state *x; int xfrm_nr = 0; int decaps = 0; // 获取skb中的spi和序列号信息 if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0) goto drop; // 进入循环进行解包操作 do { struct iphdr *iph = skb->nh.iph; // 循环解包次数太深的话放弃 if (xfrm_nr == XFRM_MAX_DEPTH) goto drop; // 根据地址, SPI和协议查找SA x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, iph->protocol, AF_INET); if (x == NULL) goto drop; // 以下根据SA定义的操作对数据解码 spin_lock(&x->lock); if (unlikely(x->km.state != XFRM_STATE_VALID)) goto drop_unlock; // 检查由SA指定的封装类型是否和函数指定的封装类型相同 if ((x->encap ? x->encap->encap_type : 0) != encap_type) goto drop_unlock; // SA重放窗口检查 if (x->props.replay_window && xfrm_replay_check(x, seq)) goto drop_unlock; // SA生存期检查 if (xfrm_state_check_expire(x)) goto drop_unlock; // type可为esp,ah,ipcomp, ipip等, 对输入数据解密 if (x->type->input(x, skb)) goto drop_unlock; /* only the first xfrm gets the encap type */ encap_type = 0; // 更新重放窗口 if (x->props.replay_window) xfrm_replay_advance(x, seq); // 包数,字节数统计 x->curlft.bytes += skb->len; x->curlft.packets++; spin_unlock(&x->lock); xfrm_vec[xfrm_nr++] = x; // mode可为通道,传输等模式, 对输入数据解封装 if (x->mode->input(x, skb)) goto drop; // 如果是IPSEC通道模式,将decaps参数置1,否则表示是传输模式 if (x->props.mode == XFRM_MODE_TUNNEL) { decaps = 1; break; } // 看内层协议是否还要继续解包, 不需要解时返回1, 需要解时返回0, 错误返回负数 // 协议类型可以多层封装的,比如用AH封装ESP, 就得先解完AH再解ESP if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0) goto drop; } while (!err); /* Allocate new secpath or COW existing one. */ // 为skb包建立新的安全路径(struct sec_path) if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { struct sec_path *sp; sp = secpath_dup(skb->sp); if (!sp) goto drop; if (skb->sp) secpath_put(skb->sp); skb->sp = sp; } if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) goto drop; // 将刚才循环解包用到的SA拷贝到安全路径 // 因此检查一个数据包是否是普通明文包还是解密后的明文包就看skb->sp参数是否为空 memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec, xfrm_nr * sizeof(xfrm_vec[0])); skb->sp->len += xfrm_nr; nf_reset(skb); if (decaps) { // 通道模式 if (!(skb->dev->flags&IFF_LOOPBACK)) { dst_release(skb->dst); skb->dst = NULL; } // 重新进入网卡接收函数 netif_rx(skb); return 0; } else { // 传输模式 #ifdef CONFIG_NETFILTER // 如果定义NETFILTER, 进入PRE_ROUTING链处理,然后进入路由选择处理 // 其实现在已经处于INPUT点, 但解码后需要将该包作为一个新包看待 // 可能需要进行目的NAT操作, 这时候可能目的地址就会改变不是到自身 // 的了, 因此需要将其相当于是放回PRE_PROUTING点去操作, 重新找路由 // 这也说明可以制定针对解码后明文包的NAT规则,在还是加密包的时候不匹配 // 但解码后能匹配上 __skb_push(skb, skb->data - skb->nh.raw); skb->nh.iph->tot_len = htons(skb->len); ip_send_check(skb->nh.iph); NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, xfrm4_rcv_encap_finish); return 0; #else // 内核不支持NETFILTER, 该包肯定就是到自身的了 // 返回IP协议的负值, 表示重新进行IP层协议的处理 // 用解码后的内层协议来处理数据 return -skb->nh.iph->protocol; #endif } drop_unlock: spin_unlock(&x->lock); xfrm_state_put(x); drop: while (--xfrm_nr >= 0) xfrm_state_put(xfrm_vec[xfrm_nr]); kfree_skb(skb); return 0; } 最后说一下返回负协议值的处理, IP上层协议的handler是在ip_local_deliver_finish()函数中调用 的: /* net/ipv4/ip_input.c */ static inline int ip_local_deliver_finish(struct sk_buff *skb) { int ihl = skb->nh.iph->ihl*4; __skb_pull(skb, ihl); /* Point into the IP datagram, just past the header. */ skb->h.raw = skb->data; rcu_read_lock(); { /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */ int protocol = skb->nh.iph->protocol; int hash; struct sock *raw_sk; struct net_protocol *ipprot; resubmit: // 协议hash值, IPv4最大支持255种协议 hash = protocol & (MAX_INET_PROTOS - 1); raw_sk = sk_head(&raw_v4_htable[hash]); /* If there maybe a raw socket we must check - if not we * don't care less */ if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash)) raw_sk = NULL; // 直接在协议数组中获取协议指针 if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) { int ret; if (!ipprot->no_policy) { if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { kfree_skb(skb); goto out; } nf_reset(skb); } // 调用协议handler ret = ipprot->handler(skb); if (ret < 0) { // 如果返回值为负, 取反后重新跳回resubmit点进行选协议处理 protocol = -ret; goto resubmit; } IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS); } else { if (!raw_sk) { if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { IP_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); } } else IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS); kfree_skb(skb); } } out: rcu_read_unlock(); return 0; } 3. 结论 虽然在2.6的native ipsec中没支持虚拟网卡,但在通道模式下也用到了netif_rx函数将解码后的数 据包重新接收处理的过程,并没有改变skb包的dev网卡参数,因此如果在该网卡上抓包,就会同时抓 到最初的加密包和解码后的明文包;而用freeswan的实现,在普通网卡上抓包抓到的是加密包,由于 freeswan在解码后将skb包的dev参数改为了ipsec*,因此是通过在ipsec*网卡上能抓到解密包。对于 传输模式,由于没有调用netif_rx函数,因此在实际网卡抓包只能抓到加密包,解密包只能在 netfilter架构中看到了。 另外,在此情况下NAT规则仍然是有效的,制定NAT规则时根据解密后的地址端口等信息来处理就可以了。识别一个明文包是否是解密过的就看skb的sp参数即可,该指针为空表示是普通明文包,非空表示是解密后的明文包。
发表评论
-
Linux内核中流量控制(24)
2011-01-10 16:33 2213本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(23)
2011-01-10 16:30 1497本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(22)
2011-01-10 16:29 1947本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(21)
2011-01-10 16:28 1362本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(20)
2011-01-10 16:27 1528本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(19)
2011-01-10 16:27 1986本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(18)
2011-01-10 16:26 1576Linux内核中流量控制(18) ... -
Linux内核中流量控制(17)
2011-01-10 16:25 1955本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(16)
2011-01-10 16:25 1813本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(15)
2011-01-10 16:24 1898本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(14)
2011-01-10 16:23 1965本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(13)
2011-01-10 16:22 2646本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(12)
2011-01-10 16:21 2115本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(11)
2011-01-10 16:21 3244本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(10)
2011-01-10 16:20 2012本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(9)
2011-01-10 16:19 1838本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(8)
2011-01-10 16:18 1504本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(7)
2011-01-10 16:18 2931本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(6)
2011-01-10 16:17 1500本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(5)
2011-01-10 16:16 1735本文档的Copyleft归yfydz所有,使用GPL发布,可以 ...
相关推荐
linux2.6_TCP包发送流程.pdf
Linux2.6 内核的 Initrd 机制解析
Linux2.6下的协议栈源代码剖析,讲解的很详细。而且是解密版可以复制打印的哦。
linux 2.6 启动流程分析 linux 2.6 启动流程分析
linux2.6内核的Initrd机制解析
详细讲述了linux2.6内核的启动流程:1解压缩;2初始化;3启动应用程序
linux 2.6源码,最广为人知的
讲述在linux内核2.6下具体的裁剪过程
"Linux 2.6内核测试及其到ARM嵌入式平台的移植" 本文将深入探讨Linux 2.6内核对嵌入式应用的影响,及其到ARM嵌入式平台的移植。Linux 2.6内核相对以前的Linux内核在可配置性和实时性方面有了很大的改进,特别是在...
由浅到深分析linux2.6内核的启动,方便对Linux各内核版本的启动分析学习。
这是我们实验室学习linux2.6的内部教程,非常经典,讲解十分到位,清楚明了,不可多得!
"Linux2.6进程调度机制的剖析" Linux2.6进程调度机制是 Linux 操作系统 的核心组件之一,对系统的整体性能有着直接的影响。 Linux2.6 内核相比 Linux2.4 内核,在进程调度机制上有了很大的改进,实现了 O(1) 调度...
linux2.6内核编译方法详述 linux2.6内核编译方法详述
Linux2.6内核的定制
LINUX 2.6内核标准教程(华清远见,河秦)(高清PDF共218M)10/10
大家学习嵌入式的朋友,有时会为IIC接口驱动伤透脑筋,这里就提供了一个在linux环境下的IIC驱动
linux2.6官方內核,便於查看各種內核驅動程序。