分类: LINUX
2015-10-27 22:00:08
原文地址:Ipsec协议实现与IP实现的整合(1) 作者:jonas_mao
原文链接这篇文章是目前查找的资料中讲述ipsec的linux实现很全面的文章,对于目前正要实现这个ipsec框架是很好的素材,一共分为两部分,这是其一,主要讲的是整个数据流的处理,还有一个讲述的则是Ipsec本身的框架 1 实现总体思想 通过改造Linux的IP协议栈源代码,使得IP实现与IPSec实现完全整合。该实现按功能分,可以分为以下几个功能模块模块:安全策略库,安全关联库,AH协议处理,ESP协议处理,算法(加密卡的实现),日志、统计、配置与审计,面向应用的接口。按处理过程分,还可以分为以下几个处理模块:进入IP层预处理、IPSec进入策略处理、路由、本机对IP包处理、IPSec进入处理、转发处理、本地IP包处理、IPSec外出处理、发送处理。 Ipsec协议实现与IP实现的整合思想:利用Netfilter的HOOK机制,在接受IP包和发送IP包的过程中,在适当的处理位置调用相关的ipsec处理模块。 1.1 Netfilter的HOOK机制的介绍 Netfilter是linux2.4内核实现数据包过滤/数据包处理/NAT等功能的抽象、通用化的框架。Netfilter框架包含以下三部分: 1、 为每种网络协议(IPv4、IPv6等)定义一套钩子函数(IPv4定义了5个钩子函数),这些钩子函数在数据报流过协议栈的几个关键点被调用。在这几个点中,协议栈将把数据报及钩子函数标号作为参数调用netfilter框架。 2、 内核的任何模块可以对每种协议的一个或多个钩子进行注册,实现挂接,这样当某个数据包被传递给netfilter框架时,内核能检测是否有任何模块对该协议和钩子函数进行了注册。若注册了,则调用该模块的注册时使用的回调函数,这样这些模块就有机会检查(可能还会修改)该数据包、丢弃该数据包及指示netfilter将该数据包传入用户空间的队列。 3、 那些排队的数据包是被传递给用户空间的异步地进行处理。一个用户进程能检查数据包,修改数据包,甚至可以重新将该数据包通过离开内核的同一个钩子函数中注入到内核中。 IP层的五个HOOK点的位置如图1所示:(ipv4) 1. NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验 和等检测), 源地址转换在此点进行; 2. NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行; 3. NF_IP_FORWARD:要转发的包通过此检测点,FORWORD包过滤在此点进行; 4. NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的目的地址转换功能(包括地址伪装)在此点进行; 5. NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行。 1.2 处理模块之间的关系 IPSec安全网关对IP报文的接收、转发和发送处理的整个过程如图2。当安全网关的网卡接收的数据报时,通过中断触发内核的中断处理程序,将网卡接收的数据报传送到内核空间,然后再通过IP层预处理程序将数据报转换为IP包。此时,我们将利用内核的Netfilter机制提供的HOOK点PRE_ROUTING,将IP包传送到IPSec进入策略处理模块。该模块将决定哪些包可以进入安全网关,哪些包需要丢弃。对于允许进入网关的IP包,将送回到路由处理模块。路由处理根据IP头决定IP包是发送到本机还是继续转发。 对于发送到本机的IP包,首先经过内核对IP包的处理,如:分片重组、选项处理等等。然后利用HOOK点LOCAL_IN,将重组的IP包传送到IPSec进入处理模块。IPSec进入处理模块将对IPSec包和非IPSec包进行区分,对于发往本机的非IPSec包将直接传送到传输层处理模块;对于IPSec包将进行认证或解密等IPSec处理,并剥去IPSec头。处理完后,将重新组装成IP包发回到IP层预处理模块。这样该IP包将重新通过路由来决定发往何处。 对于转发的IP包,首先进行转发处理,如:决定下一跳、减少TTL、对某些特殊情况发送ICMP包。然后,利用HOOK点IP_FORWARD,将IP包传送到外出IPSec外出处理模块。IPSec处理将根据策略区分IPSec包、非IPSec包以及包发往何处。对于IPSec包将丢弃。对于发往内部网的非IPSec包,直接将包传送给发送处理模块,发往内部网。对于发往外部网的IP包,将根据策略进行认证或加密等IPSec处理,最后将处理过的IP包传送到发送处理模块。 对于从安全网关传输层发送的报文,首先进行本地的IP包处理,构建IP包。然后对IP包进行路由,决定IP包的出口。路由之后将利用HOOK点LOCAL_OUTPUT,将IP包传送到IPSec外出处理模块。IPSec外出处理将根据策略决定那些包需要进行IPSec处理。对于不需要处理的IP包,直接传送到发送处理模块。对于需要进行IPSec处理的IP包,将根据策略进行认证或加密等IPSec处理,然后将IPSec包重新发回到路由处理模块,决定IPSec包将发往何处。当再次经过HOOK点时,IPSec外出处理将通过策略将处理过的IP包直接传送到发送处理模块。发送处理模块将进行分片等处理,最后将包发送到网卡。 1.3 功能模块之间的关系 各功能模块之间的关系如图3所示。安全策略库存放了由用户或系统管理员所制定的策略,策略将决定通讯的双方是否采用IPSec处理,以及决定采用何种IPSec协议、模式、算法和嵌套需求。安全关联库由一系列安全关联项组成。安全关联是两个通讯实体经过协商建立起来的一种协定。它们决定了用来保护数据包安全所需的各种参数:序号计数器;抗重播窗口;AH验证算法及其密钥;ESP加密、认证算法、密钥;安全关联的生存期;IPsec协议模式。算法库存放了多种可选的认证和加密算法,在处理时将通过安全关联中的算法项来指明所需要使用的算法。面向应用的接口提供了管理安全策略库,以及配置网关,处理日志、统计、审计信息的接口。用户或系统管理员可通过配置服务器关联全策略库,还可以进行手工注入安全关联或者启动IKE动态协商安全关联,以及对日志、统计、配置、审计信息的提取和处理。 2 各个模块的实现 2.1 处理模块的实现 2.1.1 Netfilter钩子函数 2.1.1.1 实现思想 利用Linux提供的Netfilter框架,并在Netfilter框架提供的HOOK点上注册并实现IPSec相关处理函数,使得IPSec处理能加入到IP包接收或发送处理过程的适当位置。 2.1.1.2 实现细节 Linux内核的Netfilter框架在ipv4中提供了5个HOOK点(图1),在每个HOOK点上都可以通过一个注册函数nf_register_hook(struct nf_hook_ops *reg)将自己实现的处理函数挂接到HOOK点上。这样在每一次HOOK点被激活时,都将查询并执行该HOOK点所注册的处理函数。然后在处理结束时返回相关的信息来决定被处理的包是丢弃、拒绝,还是继续进行HOOK点后面的处理。 因此我们的工作便是生成一个struct nf_hook_ops结构的实例(结构如下): struct nf_hook_ops { struct list_head list; nf_hookfn *hook; int pf; int hooknum; int priority; }; 并将该结构中的一个相关处理函数nf_hookfn(定义如下)实现为我们所需的相关IPSec处理函数: unsigned int nf_hookfn(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)); 并用nf_register_hook将该实例注册到由hooknum所指定的HOOK点上。这样,当hooknum指定的HOOK点被激活时(如在ip_rcv中的:NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish)),将根据priority所指定的优先级,依次执行在该HOOK点注册的函数,因此我们所实现的相关IPSec处理函数将得到执行。 Netfilter还提供了一系列返回信息(如下): l NF_ACCEPT 继续正常传输数据报 l NF_DROP 丢弃该数据报,不再传输 l NF_STOLEN 模块接管该数据报,不要继续传输该数据报 l NF_QUEUE 对该数据报进行排队(通常用于将数据报给用户空间的进程进行处理) l NF_REPEAT 再次调用该钩子函数 在处理结束后将通过返回信息来决定被处理的数据报在HOOK点之后应该怎样处理。 2.1.2 初始化IPSec的实现 2.1.2.1 注册HOOK点 2.1.3 进入IP包预处理 2.1.3.1 实现思想 从网卡传来的数据报在进入IP层处理之前先进行一些检查,并在此时激活第一个HOOK点:NF_IP_PRE_ROUTING,对进入本机的包进行进入前预处理。我们实现的IPSec进入策略处理模块也在此时通过HOOK点被调用执行。 2.1.3.2 实现细节 该模块首先对传入的IP包进行必要的检查: 1、通过包类型标志pkt_type检查IP包是否是其他机器(PACKET_OTHERHOST)的,如果是就丢弃。 2、检查IP包的长度是否合法。 3、检查IP版本。 4、检查校样和。 最后激活HOOK点:NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish)。然后将依次调用在该HOOK点上注册处理函数,其中包括我们实现的IPSec进入策略处理模块。。 2.1.4 IPSec进入策略处理 2.1.4.1 实现思想 该模块将根据IP报文的相关信息构造选择符。然后通过选择符查询进入安全策略库,找到第一个匹配项(策略)。再根据策略对IP包进行策略检查和处理: 1、对于外部网的进入包 drop:(增强型包过滤)不允许某些外部网的包进入;不允许某些外部网的不加密包进入;不允许某些网应该加密却未加密的包进入;不允许某些外部网的加密包进入。 reject:类似于drop,不过要对ICMP包进行特殊处理。 accept:对某些外部网的不加密包或加密包允许通过,对已经经过IPSec处理的包(用于IPSec嵌套的情况)允许通过。 2、对于内部网的进入包 drop:对某些内部网机器的IP包不允许通过 accept:允许内部网的IP包通过 最后对于通过的包,传回到ip_rcv_finish继续处理 2.1.4.2 实现细节 1、从IP包中提取源/目的IP地址、源/目的端口、下一层协议等信息。 2、根据这些提取的信息查询进入安全策略库,找到匹配的策略项。 3、根据策略项所指定的策略进行处理: 丢弃:返回NF_DROP,Netfilter机制将丢弃该数据包,不再传输。并将该事件记录到日志中。 拒绝:返回NF_STOLEN,Netfilter机制将接管该数据包,不再继续传输。并将该事件记录到日志中。 接收:返回NF_ACCEPT,Netfilter机制将正常传输该数据包:将该包传送给ip_rcv_finish。其中对于IPSec包需要作上标记(利用标记变量nmask),表明该包需要在以后进行IPSec处理。 2.1.5 路由 2.1.5.1 实现思想 根据IP包的源地址、目的地址和服务类型(tos)等信息查找路由表,找到该IP包对应的路由项,并记录该项。在路由项中指出了IP包接下来该如何处理:对于本地IP包,将交给本机的IP处理模块ip_local_deliver;对于非本机接收包,适合转发的将交给转发处理模块ip_forward,其他情况将作特殊处理。 2.1.5.2 实现细节 1、将从IP包中提取的源地址、目的地址和服务类型值,通过适当的散列算法产生散列值。 2、根据散列值在路由缓存散列表中查找散列位置 3、进一步根据源地址、目的地址、输入接口、服务类型在散列链中找到正确的路由项。 4、根据路由项中指定的输入处理函数,调用适当的函数:对于本地IP包调用ip_local_deliver;对于其他IP包调用ip_forward。 2.1.6 本机对IP包处理 2.1.6.1 实现思想 对经过路由传送来的IP包,首先检查是否存在分片。如果存在分片,必须缓存IP分片包。等所有的分片到齐时,进行分片重组处理,将分片合成完整的IP报文。然后激活HOOK点:NF_IP_LOCAL_IN,对进入IP包进行相关处理。我们实现的IPSec进入处理模块也在此时调用。 2.1.6.2 实现细节 1、 检查IP包的3位标志字段,当IP包片偏量(frag_off)第14位(IP_MF)为1时, 表示该IP包有后继分片。这时将进行分片重组处理ip_defrag。 2、 分片重组处理: l 根据分片包的16位标识、源地址、目的地址、协议字段计算分片包的散列值。 l 根据散列值,定位该包在分片链中的位置。 l 如果有分片链,说明已有其他分片到达。将该分片插入到对应的分片队列中。 l 检查该分片是否是最后一个分片,分片是否都到齐了。如果都满足,则将分片重组为一个完整的IP包。 l 返回新的IP包。 3、 激活HOOK点:NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL, ip_local_deliver_finish)。然后将依次调用在该HOOK点上注册处理函数,其中包括我们实现的IPSec进入处理模块。 2.1.7 IPSec进入处理 2.1.7.1 实现思想 首先检查nmask标记,判断该IP包是否是一个IPSec包。如果不是,则将利用返回值NF_ACCEPT, 继续正常传输该数据报到ip_local_deliver_finish。如果是,则将进行IPSec进入处理,并在处理完后调用ip_rcv将IP包传送回进入IP包预处理模块,此时应该返回NF_DROP,使该包不在向上传输。 IPSec进入处理首先将从IP包中提取目的地址、下一协议、安全参数索引(spi)信息,并根据这些信息查找安全关联库(SAD),找到对应的唯一的一个SA。然后根据找到的SA进行:SA状态处理、SA生存期处理、重播窗口处理、模式处理、相关IPSec协议处理(AH或ESP)。当处理完一个SA后,必须要标记处理过的SA(用于后期的进入策略匹配),并检查是否存在SA串(即多个SA)。如果有,则循环处理SA串,直到遇到传输层协议头或者非本机的IP头(表明SA串上的所有SA都处理完毕)。 最后根据处理过的IP包的相关信息查找安全策略库(SPD),找到该IP包所对应的进入策略(该策略应该与发送端的外出策略是相同的)。然后检查该策略所指定的SA串是否与处理过并标记的SA串相同,从而判断是否进行了所要求的IPSec处理。如果不相同,则丢弃该IP包,并进行日志记录。 2.1.7.2 实现细节 1、 检查nmask标记(该标记在进入策略处理时被设置上)。如果不是IPSec包,返回NF_ACCEPT;如果是,继续进行IPSec进入处理。 2、 提取IP头中的目的地址、下一协议,通过下一协议字段信息,提取IPSec头中的安全参数索引(spi)信息。 3、 根据,proto,spi>三元组查找安全关联库(SAD)。如果没找到对应的SA(或SA串),则将该IP包丢弃,并记录日志信息;如果找到了对应的SA(或SA串),则将根据SA(或SA串)对IPSec包进行下面的处理。 4、 检查SA的状态:对于幼稚(SADB_SASTATE_LARVAL)和死亡(SADB_SASTATE_DEAD)状态的包将丢弃,并记录日志信息 5、 检查SA的生存期。生存期分为以下几种:字节数、包数、使用时间、增加时间,并且对于每一种生存期还有软硬之分。如果是硬生存期过期了,则将激活IKE模块动态协商一个新的SA,将过期的SA(或SA束)删除,并将IP包丢弃,并记录日志信息;如果是软生存期过期了,同样要激活IKE模块动态协商一个新的SA,但此时并不删除SA(或SA束),而是将SA的状态标记为正在死亡(SADB_SASTATE_DYING)。而且不丢弃该数据包,继续进行处理。 6、 检查重播窗口:检查时将判断到达的IPSec包的序列号是否小于该网关所记录的处理过的最大IPSec包的序列号。如果小,则再检查序号的差值是否超过了窗口的大小。如果没有超过,则检查窗口,判断该IPSec包是否已经到达过,如果已经到达过,则将该包丢弃;如果没到达,则接收。如果到达的IPSec包的序列号大于处理过的最大序列号,则接收,并继续处理。 7、 根据SA指定的IPSec协议(AH或ESP)、算法和密钥进行认证或(和)解密处理。 8、 更新重播窗口:此时检查如果被处理的IPSec包的序列号大于网关处理过的最大序列号,并且序号的差值小于窗口大小,则更新窗口,标记该到达并处理过的包。最后将网关处理的最大序列号更新为该被处理IPSec包的序列号。 9、 更新SA的生存期:字节数、包数、使用时间。 10、 记录下该SA信息,以便与以后进行进入策略检查。 11、 提取IPSec头指出的下一协议:如果还是IPSec协议,说明有多个SA(SA串)对该数据包进行了处理,则需要利用原IP头和处理过的数据区组成新的IP包,进行循环处理;如果是IP协议,说明SA(或SA串)已经处理完毕,则需要根据内部的IP头信息组建新的IP包,并跳出循环进入后继的处理。 12、 进行进入策略匹配:根据新的IP包信息(源地址/目的地址、源端口/目的端口、协议)查找安全策略库(SPD),找到该包所对应的第一个进入处理策略(可能匹配多个)。检查该策略所指定的SA(或SA串)即其应用的顺序是否与10中记录过的 SA(或SA串)以及顺序相同。如果相同,则说明满足进入策略允许该数据报进入安全网关;如果不相同,则将再次查找策略库(遍历策略库),直到匹配成功。如果最后还没有找到正确的进入策略,则将丢弃该数据报,并记录日志信息。 13、 最后调用IP包与处理模块(ip_rcv),重新对新生成的IP包进行处理。 14、 返回NF_DROP,说明IP包不从该HOOK点返回。 2.1.8 转发处理 2.1.8.1 实现思想 对于非本地的IP包,需要将它进行转发。转发处理首先要检查IP头的TTL字段不能小于1,否则将向该IP包的发送者发送ICMP超时差错包。减小TTL值。接下来提取该IP包的路由信息(在转发前已经记录),根据路由信息获得该IP包的外出设备。然后根据外出设备的MTU和IP头的分片标志,判断允许分片,如果需要分片且不允许分片(DF位设置为1),则发送ICMP_DEST_UNREACH目的不可达ICMP报文。最后激活HOOK点:IP_FORWARD,对转发IP包进行相关处理。我们实现的IPSec外出处理模块也在此时调用。 2.1.8.2 实现细节 1、 检查IP头的TTL字段:如果小于1,则向该IP包的发送者发送ICMP生存期超时差错包,丢弃该包 2、 减少该IP包的TTL值。 3、 提取转发前记录的路由信息,找到外出设备。 4、 根据外出设备的MTU和IP头的分片标志,判断允许分片,如果需要分片且不允许分片(DF位设置为1),则发送ICMP_DEST_UNREACH目的不可达ICMP报文。 5、 激活HOOK点:NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, dev2, ip_forward_finish);调用我们实现的IPSec外出处理模块。 2.1.9 本地IP包处理 2.1.9.1 实现思想 对于本地传输层(TCP/UDP)产生的包,首先将进行路由,然后组装成IP包。最后包激活HOOK点5:NF_IP_LOCAL_OUT。在这个HOOK点上将调用我们实现的IPSec外出处理模块。 2.1.9.2 实现细节 1. 查找路由 2. 填充IP头,组装IP包 3. 激活HOOK点5:NF_IP_LOCAL_OUT, 对于TCP:NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, ip_queue_xmit2) 对于UDP:NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,ip_send) 2.1.10 IPSec外出处理 2.1.10.1 实现思想 IPSec的外出处理函数挂在HOOK点:NF_IP_FORWARD 、NF_IP_LOCAL_OUT上。在这里首先将根据IP包的相关信息查找安全策略库(SPD),找到该IP包对应的外出策略。策略将决定对该IP包的几种处理:ACCEPT; IPSEC SAList; DROP; TRAP; HOLD。ACCEPT表示对IP包不做任何IPSec处理,允许IP包直接通过。IPSEC SAList表示将根据SAList指示的安全关联串(SA串)对IP包进行IPSec处理。DROP表示将丢弃策略不允许外出的IP包,或者先前试图等待SA协商,但SA却不能协商成功的包。TRAP表示要求更新一个安全关联(SA),此时将设置SA为HOLD,并通过发送PF_KEY消息来触发IKE守护进程进行安全关联(SA)的协商。HOLD表示SA还未协商成功,要求丢弃IP包。 对于可以进行IPSec外出处理的包,根据策略提供的信息,查找安全关联库(SAD),找到该IP包应该应用的安全关联(SA或SA串)。如果没有找到SA,则将通过发送PF_KEY消息来触发IKE守护进程进行安全关联(SA或SA串)的协商,此时IP包被暂时保持,直到sa创建或超时。如果找到了SA(或SA串),则检查SA的状态、生存期。然后进行IPSec协议处理,根据SA(或SA串)所指定的协议(AH或ESP),循环对IP包进行认证、加密等处理。SA串处理完毕后,将重新构建IP包,并对新的IP包重新进行路由。最后返回NF_ACCEPT,表示该IP包将进入激活HOOK点时指定的处理函数:ip_queue_xmit2、ip_send,进入发送处理模块。 2.1.10.2 实现细节 1. 查找SPD(外出策略)库,找到匹配项的所定义的目标动作。 1) ACCEPT:返回NF_ACCEPT,即包不用经过任何加工处理,直接通过。 2) DROP:返回NF_DROP,丢掉此包不再传输 3) TRAP:发送PF_key 消息ACCQUIRE给IKE守护进程,在SPD中把它替换成HOLD,返回NF_STOLEN,不继续传输该数据报。 4) HOLD:截取符合此策略的包,更新以前截获的包,返回NF_STOLEN,不继续传输该数据报。 5) IPSEC Salist:进行IPSec处理。以下便是详细的步骤说明。 2. 根据策略提供的信息,查找安全关联库(SAD),寻找该IP包应该应用的安全关联(SA或SA串)。如果没有找到SA,则将通过发送PF_KEY消息来触发IKE守护进程进行安全关联(SA或SA串)的协商,此时IP包被暂时保持,直到sa创建或超时。 3. 如果找到了SA(SA串),检查当前SA的状态,对于已经过时的SA,要释放SA和这个SA包。 4. 设置重播窗口。 5. 检查SA的生命期,包括使用时间,已处理的字节数,处理的IP包数目。对于已经过生命期的SA,要删除,发PF_key消息给IKE。(*IKE应该自己维护SA ,更新删除应该能自动进行不需内核干涉或提醒,但是要实现对于字节的限制应该只能由内核告诉IKE进行更新) 6. 根据SA中的协议设置AH或ESP头,然后转到SA链的下一个SA,重复从4开始的处理。 7. 设置新的MTU(如果实现了PMTU机制,要做相应的处理) 8. 根据SA指定的协议(AH或ESP)设置头空间和尾空间,并使用SA规定的协议中应该使用的加密或验证算法处理IP包数据区内容。 9. 处理结束后调整IP头重新设置校验和。 10. 更新SA的生命期 11. 对于SA串将循环处理SA串上的每一个SA 12. 对新的IP包重新进行路由。 13. 返回NF_ACCEPT,将IP包传送到进入激活HOOK点时指定的处理函数:ip_queue_xmit2、ip_send。进入发送处理模块。 |